├── public ├── lucas.jpeg ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── src ├── app │ ├── favicon.ico │ ├── post │ │ └── page.tsx │ ├── not-found.ts │ └── layout.tsx ├── sections │ └── Home │ │ ├── components │ │ ├── ContextPOC │ │ │ ├── components │ │ │ │ ├── Container │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ └── Container.tsx │ │ │ │ └── Title │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ └── Title.tsx │ │ │ ├── ContextPOC.tsx │ │ │ ├── ContextPOC.module.scss │ │ │ └── providers │ │ │ │ └── SwitcherProvider.tsx │ │ ├── StatePOC │ │ │ ├── components │ │ │ │ ├── Container │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ └── Container.tsx │ │ │ │ └── Title │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ └── Title.tsx │ │ │ ├── StatePOC.module.scss │ │ │ └── StatePOC.tsx │ │ ├── ZustandPOC │ │ │ ├── components │ │ │ │ ├── Container │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ └── Container.tsx │ │ │ │ └── Title │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ └── Title.tsx │ │ │ ├── stores │ │ │ │ └── SwitcherStore.tsx │ │ │ ├── ZustandPOC.tsx │ │ │ └── ZustandPOC.module.scss │ │ ├── StateWithMemoPOC │ │ │ ├── components │ │ │ │ ├── Container │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ └── Container.tsx │ │ │ │ └── Title │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ └── Title.tsx │ │ │ ├── StateWithMemoPOC.module.scss │ │ │ └── StateWithMemoPOC.tsx │ │ └── _StressTest │ │ │ ├── shared │ │ │ ├── Child │ │ │ │ └── Child.tsx │ │ │ └── MemoizedChild │ │ │ │ └── MemoizedChild.tsx │ │ │ ├── components │ │ │ ├── ZustandStress │ │ │ │ ├── components │ │ │ │ │ ├── Container │ │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ │ └── Container.tsx │ │ │ │ │ └── Title │ │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ │ └── Title.tsx │ │ │ │ ├── stores │ │ │ │ │ └── useSwitcherStore.tsx │ │ │ │ ├── ZustandStress.module.scss │ │ │ │ └── ZustandStress.tsx │ │ │ ├── StateStress │ │ │ │ ├── components │ │ │ │ │ ├── Container │ │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ │ └── Container.tsx │ │ │ │ │ └── Title │ │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ │ └── Title.tsx │ │ │ │ ├── StateStress.module.scss │ │ │ │ └── StateStress.tsx │ │ │ └── StateWithMemoStress │ │ │ │ ├── components │ │ │ │ ├── Container │ │ │ │ │ ├── Container.module.scss │ │ │ │ │ └── Container.tsx │ │ │ │ └── Title │ │ │ │ │ ├── Title.module.scss │ │ │ │ │ └── Title.tsx │ │ │ │ ├── StateWithMemoStress.module.scss │ │ │ │ └── StateWithMemoStress.tsx │ │ │ ├── StressGroup.module.scss │ │ │ └── StressGroup.tsx │ │ ├── Home.module.scss │ │ ├── constants.ts │ │ └── Home.tsx ├── components │ ├── Icon │ │ ├── Icon.module.scss │ │ ├── Icon.tsx │ │ └── icons │ │ │ ├── Linkedin.tsx │ │ │ └── Github.tsx │ ├── Avatar │ │ ├── Avatar.module.scss │ │ └── Avatar.tsx │ ├── Footer │ │ ├── Footer.tsx │ │ └── Footer.module.scss │ ├── CodeBlock │ │ ├── CodeBlock.module.scss │ │ └── CodeBlock.tsx │ ├── Header │ │ ├── Header.module.scss │ │ └── Header.tsx │ ├── RenderCount │ │ ├── RenderCount.module.scss │ │ └── RenderCount.tsx │ └── Switch │ │ ├── Switch.tsx │ │ └── Switch.module.scss ├── styles │ └── globals.scss ├── utils │ └── classNames.ts └── middleware.ts ├── next.config.ts ├── eslint.config.mjs ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /public/lucas.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasca2/state-poc/HEAD/public/lucas.jpeg -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasca2/state-poc/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sections/Home/components/ContextPOC/components/Container/Container.module.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | > div { 3 | top: 50%; 4 | transform: translateY(-50%); 5 | } 6 | } -------------------------------------------------------------------------------- /src/sections/Home/components/StatePOC/components/Container/Container.module.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | > div { 3 | top: 50%; 4 | transform: translateY(-50%); 5 | } 6 | } -------------------------------------------------------------------------------- /src/sections/Home/components/ZustandPOC/components/Container/Container.module.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | > div { 3 | top: 50%; 4 | transform: translateY(-50%); 5 | } 6 | } -------------------------------------------------------------------------------- /src/sections/Home/components/StateWithMemoPOC/components/Container/Container.module.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | > div { 3 | top: 50%; 4 | transform: translateY(-50%); 5 | } 6 | } -------------------------------------------------------------------------------- /src/components/Icon/Icon.module.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | color: #fff; 3 | 4 | width: 24px; 5 | height: 24px; 6 | 7 | & svg { 8 | width: 100%; 9 | height: 100%; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/sections/Home/components/_StressTest/shared/Child/Child.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Child = ({ value }: { value: string }) => { 4 | return
14 | {tokens.map((line, i) => (
15 |
16 | {line.map((token, key) => (
17 |
18 | ))}
19 |
20 | ))}
21 |
22 | )}
23 | React.js
31 |
33 | Primeiramente, vamos pensar em um cenário simples onde temos que
34 | gerenciar um estado que será compartilhado entre dois componentes.
35 |
36 | Então teremos um componente pai, que irá renderizar dois componentes
37 | filhos, e esses componentes filhos precisam compartilhar o mesmo estado.
38 |
39 | Um deles vai receber apenas um texto (que poderia vir de uma API) e o
40 | outro vai ser um Switcher que vai servir apenas pra
41 | conseguir simular essa mudança de estado.
42 |
useState;
46 |
48 | Acredito que a primeira coisa que vem à mente é usar o{" "}
49 | useState para gerenciar o estado no componente pai e passar
50 | os valores e funções para os componentes filhos, que é a abordagem mais
51 | comum de gerenciamento de estados.
52 |
53 | Algo mais ou menos assim:
54 |
58 | Se notar nessa abordagem, cada vez que o state do{" "}
59 | switcherValue muda, todos os componentes são
60 | re-renderizados novamente, até mesmo o Title que não tem
61 | nada a ver com isso!
62 |
64 | Isso acontece por que toda vez que um estado muda, o componente que o
65 | contém é re-renderizado, e como o App é o componente pai de
66 | todos os outros, todos eles são re-renderizados juntos.
67 |
69 | Existe uma forma de otimizar isso utilizando o React.memo,
70 | que basicamente faz com que um componente só seja re-renderizado se
71 | alguma propriedade que ele recebe for alterada.
72 |
73 | Algo mais ou menos assim:
74 |
77 | Com essa simples abordagem, ele já resolve o problema de re-renderizar o{" "}
78 | Title toda vez que o switcherValue muda.
79 |
82 | Porém note que o App continua re-renderizando toda vez que
83 | o switcherValue muda... Isso é um problema? não
84 | necessariamente, por que nesse caso o unico componente que vai ter
85 | impacto de fato é o Container, que é o esperado. O{" "}
86 | App é re-renderizado, mas ele só vai causar o re-render do{" "}
87 | Container que realmente deve ser re-renderizado, já que seu
88 | estado mudou, enquanto o Title vai continuar lá intacto por
89 | conta do React.memo.
90 |
createContext;
94 |
96 | Um problema que temos quando começamos a usar a abordagem de{" "}
97 | useState é que a medida que a aplicação cresce, a
98 | quantidade de props que precisamos passar para os componentes filhos
99 | também cresce, e isso pode se tornar um problema a longo prazo.
100 |
102 | E então é que surge a brilhante ideia de resolver isso utilizando um{" "}
103 | Context do React, que basicamente é um objeto que vai ser
104 | compartilhado entre todos os componentes que estão dentro dele.
105 |
107 | Mas ao mesmo tempo que o código fica muito mais “limpo“ e organizado, 108 | ele também é muito perigoso e pode ser um inimigo pra performance da sua 109 | aplicação. 110 |
111 |
112 | Vamos pegar e passar toda essa lógica do state e colocar dentro de um{" "}
113 | Context então pra ver como ele se comporta.
114 |
118 | Então, como podemos ver, o código realmente fica visualmente mais limpo,
119 | por que não precisamos mais passar as props para os componentes filhos.
120 |
121 | Dentro de cada componente filho a gente simplesmente chamaria o contexto
122 | e pegaria o estado que a gente precisa.
123 |
127 | Muito mais organizado, mas vamos ver na prática como isso se comporta? 128 |
129 |
131 | Note que o App realmente parou de re-renderizar, ok! Mas o{" "}
132 | Title está re-renderizando toda vez que o{" "}
133 | switcherValue muda, e isso é um problema.
134 |
136 | Isso acontece por que o React não consegue identificar qual
137 | estado do contexto aquele componente depende, então ele re-renderiza
138 | toda vez que o estado do contexto muda. E não há React.memo{" "}
139 | que resolva, por que nesse caso não tem nenhuma propriedade que muda, o
140 | que muda é o estado do contexto.
141 |
Como resolver isso então? Bom, a resposta é: Depende...
143 |
144 | Em alguns casos a gente pode dividir o contexto em vários contextos
145 | menores, e assim a gente consegue controlar melhor o que cada componente
146 | depende. Mas essa abordagem não é muito escalável, por que se o estado é
147 | muito complexo, acaba que é necessário criar diversos contextos e acaba
148 | deixando o código muito complexo e confuso.
149 |
150 | Então geralmente o Context é mais recomendado para estados
151 | que são mais simples ou para estados que não são alterados
152 | frequentemente como um tema(light/dark) por exemplo.
153 |
Zustand;
156 |
158 | Se usar useState, fica muito verboso, se usar{" "}
159 | createContext, não fica performático, o que usar então?
160 |
162 | Nesses casos, a gente pode optar por usar um gerenciador de estados mais 163 | robusto, como por exemplo um Redux, Zustand, etc...{" "} 164 |
165 |
166 | A diferença entre esses gerenciadores de estados robustos e o{" "}
167 | Context é que a gente consegue observar apenas um pedaço do
168 | estado invés de sempre observar o estado inteiro.
169 |
Nesse exemplo vou usar o Zustand pela facilidade da implementação:
171 |174 | Note que até mesmo nosso arquivo principal fica mais limpo e organizado, 175 | por que ele não precisa saber sobre os estados dos componentes filhos. 176 |
177 |
180 | Dessa forma, o componente Title vai re-renderizar apenas se
181 | o estado title for alterado, por que agora ele não está
182 | mais observando o restante dos estados
183 |
185 | O mesmo vale pro Container, se por alguma razão o estado{" "}
186 | title fosse alterado, isso não o afetaria em nada.
187 |
Mas vamos ver na prática como isso fica:
189 |
191 | Usando um gerenciador mais robusto, a gente até consegue evitar o uso do{" "}
192 | React.memo por que não é mais necessário já que o
193 | componente pai não está mais sendo re-renderizado desnecessariamente
194 | (como acontece usando o useState)
195 |
198 | Na maioria das vezes, um simples useState é o suficiente
199 | pra resolver os problemas simples do dia a dia, se souber trabalhar bem
200 | com ele, ele é tão performático quanto qualquer outro gerenciador de
201 | estados.
202 |
204 | Porém usar React.memo demais não é muito legal, por que ele
205 | também tem um custo de performance, então é sempre bom usar com
206 | moderação.
207 |
209 | Do mesmo jeito também que não é legal usar um Redux/Zustand/etc.. pra 210 | qualquer estado simples da nossa aplicação, a gente estaria adicionando 211 | uma complexidade desnecessária. 212 |
213 |214 | Mas que custo é esse? falar é fácil, então vamos fazer um teste de 215 | stress e ver na prática! 216 |
217 |
224 | Em resumo, a melhor abordagem vai depender do seu caso de uso, se você
225 | tem um estado simples, use um useState, se você tem um
226 | estado mais complexo, use um gerenciador de estados mais robusto. Se
227 | você tem um estado interno e quer usar um useState de uma
228 | forma mais limpa, use um Context.
229 |
231 | Na maioria das vezes o useState é o suficiente, se você
232 | notar que seu código está começando a ficar muito complexo e confuso, aí
233 | sim é hora de pensar em usar um gerenciador de estados mais robusto.
234 |
236 | Mas tome cuidado com re-renderizações desnecessárias antes que vire uma 237 | bola de neve e você não consiga mais controlar. 238 |
239 |240 | Fique a vontade pra instalar o{" "} 241 | repositório e fazer mais testes por 242 | conta própria! 243 |
244 | 245 |