Single Source of Truth em componentes React
Neste artigo irei explicar o que é Single Source of Truth e como ele pode ser utilizado para prevenir possíveis bugs e ainda melhorar a legibilidade dos seus componentes.
Single Source of Truth (ou SSOT) é uma prática de estruturar as informações em sua aplicação de forma que haja um ponto central, que é o estado em si, e ramificações desse estado, que são derivações da informação contida no estado. Em termos simples, o SSOT refere-se a ter um único local onde o estado da aplicação é armazenado. Em vez de espalhar o estado por diferentes partes do código, você centraliza todas as informações relevantes em um único ponto. Isso oferece várias vantagens:
O primeiro passo para entender o SSOT é saber o que é e como desenvolver estados centralizados. Para compreender melhor o conceito aplicado no contexto de componentes React, vamos primeiro dar uma olhada no exemplo abaixo:
Atenção! Essa não é forma recomendada de implementar esses tipos de funcionalidades, o código utilizado aqui é apenas para fins de demonstração.
O componente FruitList
possui dois estados: query
e filteredFruits
. O primeiro estado guarda o texto que o usuário digitou e o segundo guarda a lista de frutas filtrada pelo texto. A primeira vista o código aparenta não ter problemas, entretanto conforme são implementadas mais regras algumas dificuldades começam a aparecer.
Vamos fazer um ajuste no componente e adicionar um botão para limpar a pesquisa. Uma funcionalidade relativamente simples:
Após implementar a funcionalidade já nos deparamos com o primeiro problema: o campo de texto foi limpo, porém os resultados seguem filtrados. Algumas das soluções possíveis para o problema seriam:
setFilteredFruits
dentro de clear
para reiniciar a lista sem o filtro aplicadofilter
passando um valor vaziosetFilteredFruits
para dentro de um useEffect
Todas essas abordagens seguem o caminho de tentar sincronizar os estados query
e filteredFruits
. Em ambos os casos a solução seria trivial: uma linha de código e o trabalho está feito. Entretanto, conforme novas regras de negócio são adicionadas e a complexidade do projeto cresce, a sincronização de estados passa a se tornar mais difícil e consequentemente começa a ser a causa raiz de muitos bugs.
Vamos analisar outro exemplo do mesmo componente com o código refatorado, restando apenas um estado que é a fonte única da verdade:
Agora nesse exemplo a variável filteredFruits
, que antes era um estado por si só, tornou-se apenas o resultado de uma expressão baseada no estado query
. Dessa forma, temos a garantia de que o valor de filteredFruits
sempre estará de acordo com o valor de query
, sem necessidade de nenhuma sincronização.
”Mas e a performance?”
Vamos lembrar da famosa frase: “otimização prematura é a raíz de todo mal”.
No exemplo exibido temos uma lista que possui poucos itens dentro de um componente que será renderizado apenas quando o filtro mudar.
Prefira o código simples e legível e apenas otimize se você tem meios de afirmar que aquela otimização se faz necessária. Mesmo em manipulação de arrays o custo de performance as vezes é negligível. Caso seja realmente necessário, o hookuseMemo
pode ser utilizado para que a expressão não impacte em termos de performance.
O conceito SSOT não se limita apenas a um componente, você pode e deve pensar a centralização dos estados através de sua aplicação. Os benefícios são os mesmos, porém é necessário pensar nas abordagens possíveis e qual a ideal para seu caso de uso.
A abordagem mais prática é simplesmente passar o estado via props. O componente pai possui o estado e fornece o valor atual via prop para os componentes filhos. Os componentes filhos, por sua vez, chamam callbacks para comunicar que desejam atualizar o valor do estado e o componente pai processa a solicitação.
Essa abordagem também é conhecida como componentes controlados.
Essa forma de passar o estado adiante tem pontos positivos e negativos. Alguns pontos positivos:
Alguns pontos negativos:
Existe outra abordagem que mitiga os pontos negativos de passar o estado através de props, utilizando contexto do React. O contexto pode ser utilizado para transmitir o valor de um estado com toda a árvore de componentes filhos, mantendo os mesmos sincronizados.
Vamos analisar os pontos positivos dessa abordagem:
Agora alguns pontos negativos:
É muito difícil definir uma regra geral de qual abordagem escolher em cada situação, pois cada problema tem suas especificidades, mas aqui vou deixar algumas recomendações:
Em componentes genéricos e reutilizáveis (componentes de design system, por exemplo), quando o objetivo é trafegar estado interno ou quando o estado abrange muitas partes do sistema, o recomendável é utilizar contexto.
Por exemplo, um componente de TabBar
pode passar o estado da aba ativa para os componentes Tab
filhos através de contexto, sem a necessidade de cada um deles receber essa informação via props.
Em componentes mais especializados ou quando o estado é externo, é recomendado utilizar props e componentes controlados:
Mas como nada é preto no branco, existem situações onde ambas as abordagens possam ser aplicadas:
Espero que esse artigo ajude você a escrever componentes mais fáceis de manter e consequentemente te poupe dor de cabeça no futuro. Até a próxima!