Do tradutor:
Apresento uma tradução gratuita de um artigo sobre como implementar uma solução eficaz para substituir o Redux pelo contexto e ganchos do React. A indicação de erros na tradução ou no texto é bem-vinda. Aproveite a sua visualização.
Desde o lançamento da nova API de contexto no React 16.3.0, muitas pessoas se perguntaram se a nova API é boa o suficiente para considerá-la uma substituição do Redux? Eu pensei a mesma coisa, mas não entendi completamente, mesmo após o lançamento da versão 16.8.0 com ganchos. Eu tento usar tecnologias populares, o caminho nem sempre é entender toda a gama de problemas que eles resolvem, por isso estou muito acostumado ao Redux.
Por isso, me inscrevi no boletim informativo de 
Kent C. Dodds e descobri alguns emails sobre o assunto de gerenciamento de contexto e estado. Eu comecei a ler .... e leia ... e após 5 postagens no blog, algo clicou.
Para entender todos os conceitos básicos por trás disso, 
criaremos um botão, clicando no qual receberemos piadas com 
icanhazdadjoke e as exibiremos. Este é um exemplo pequeno, mas suficiente.
Para preparar, vamos começar com duas dicas aparentemente aleatórias.
Primeiro, deixe-me apresentar meu amigo 
console.count :
 console.count('Button')  
Adicionaremos uma chamada 
console.count a cada componente para ver quantas vezes ela está sendo renderizada. Muito legal, né?
Em segundo lugar, quando um componente React é renderizado novamente, ele 
não renderiza novamente o conteúdo passado como 
children .
 function Parent({ children }) { const [count, setCount] = React.useState(0) console.count('Parent') return ( <div> <button type="button" onClick={() => { setCount(count => count + 1) }}> Force re-render </button> {children} </div> ) } function Child() { console.count('Child') return <div /> } function App() { return ( <Parent> <Child /> </Parent> ) } 
Após alguns cliques no botão, você verá o seguinte conteúdo no console:
 Parent: 1 Child: 1 Parent: 2 Parent: 3 Parent: 4 
Lembre-se de que essa é uma maneira geralmente negligenciada de melhorar o desempenho do seu aplicativo.
Agora que estamos prontos, vamos criar o esqueleto do nosso aplicativo:
 import React from 'react' function Button() { console.count('Button') return ( <button type="button"> Fetch dad joke </button> ) } function DadJoke() { console.count('DadJoke') return ( <p>Fetched dad joke</p> ) } function App() { console.count('App') return ( <div> <Button /> <DadJoke /> </div> ) } export default App 
Button deve receber um gerador de ações (aprox. Action Creator. A tradução é retirada da 
documentação do 
Redux em russo ) que receberá uma anedota. 
DadJoke deve obter o estado e o 
App exibir os dois componentes usando o contexto do provedor.
Agora crie um componente personalizado e chame-o de 
DadJokeProvider , que dentro dele gerenciará o estado e agrupará os componentes filhos no Provedor de Contexto. Lembre-se de que a atualização de seu estado não renderiza novamente o aplicativo inteiro devido à otimização dos filhos acima no React.
Portanto, crie um arquivo e chame-o de 
contexts/dad-joke.js :
 import React from 'react' const DadJokeContext = React.createContext() export function DadJokeContextProvider({ children }) { const state = { dadJoke: null } const actions = { fetchDadJoke: () => {}, } return ( <DadJokeContext.Provider value={{ state, actions }}> {children} </DadJokeContext.Provider> ) } 
Também exportamos 2 ganchos para obter o valor do contexto.
 export function useDadJokeState() { return React.useContext(DadJokeContext).state } export function useDadJokeActions() { return React.useContext(DadJokeContext).actions } 
Agora podemos implementar isso:
 import React from 'react' import { DadJokeProvider, useDadJokeState, useDadJokeActions, } from './contexts/dad-joke' function Button() { const { fetchDadJoke } = useDadJokeActions() console.count('Button') return ( <button type="button" onClick={fetchDadJoke}> Fetch dad joke </button> ) } function DadJoke() { const { dadJoke } = useDadJokeState() console.count('DadJoke') return ( <p>{dadJoke}</p> ) } function App() { console.count('App') return ( <DadJokeProvider> <Button /> <DadJoke /> </DadJokeProvider> ) } export default App 
Aqui! Graças à API que criamos usando os ganchos. Não faremos mais alterações nesse arquivo ao longo da postagem.
Vamos começar a adicionar funcionalidades ao nosso arquivo de contexto, começando com o estado de 
DadJokeProvider . Sim, podemos apenas usar o gancho 
useState , mas vamos gerenciar nosso estado através do 
reducer , simplesmente adicionando a conhecida e amada funcionalidade 
Redux para nós.
 function reducer(state, action) { switch (action.type) { case 'SET_DAD_JOKE': return { ...state, dadJoke: action.payload, } default: return new Error(); } } 
Agora podemos passar esse redutor para o gancho 
useReducer e obter piadas com a API:
 export function DadJokeProvider({ children }) { const [state, dispatch] = React.useReducer(reducer, { dadJoke: null }) async function fetchDadJoke() { const response = await fetch('https://icanhazdadjoke.com', { headers: { accept: 'application/json', }, }) const data = await response.json() dispatch({ type: 'SET_DAD_JOKE', payload: data.joke, }) } const actions = { fetchDadJoke, } return ( <DadJokeContext.Provider value={{ state, actions }}> {children} </DadJokeContext.Provider> ) } 
Deveria funcionar! Clique no botão deve receber e exibir piadas!
Vamos verificar o console:
 App: 1 Button: 1 DadJoke: 1 Button: 2 DadJoke: 2 Button: 3 DadJoke: 3 
Ambos os componentes são renderizados novamente cada vez que o estado é atualizado, mas apenas um deles realmente o usa. Imagine um aplicativo real no qual centenas de componentes usem apenas ações. Seria bom se pudéssemos fornecer todas essas renderizações opcionais?
E aqui entramos no território da relativa igualdade, então um pequeno lembrete:
 const obj = {}  
Um componente que usa o contexto será renderizado novamente sempre que o valor desse contexto for alterado. Vejamos o significado do nosso provedor de contexto:
 <DadJokeContext.Provider value={{ state, actions }}> 
Aqui, criamos um novo objeto durante cada renderizador, mas isso é inevitável, porque um novo objeto será criado toda vez que executarmos uma ação ( 
dispatch ), por isso é simplesmente impossível armazenar em cache ( 
memoize ) esse valor.
E tudo parece o fim da história, certo?
Se olharmos para a função 
fetchDadJoke , a única coisa que ela usa no escopo externo é 
dispatch , certo? Em geral, vou contar um pequeno segredo sobre as funções criadas em 
useReducer e 
useState . Por uma questão de brevidade, usarei 
useState como um exemplo:
 let prevSetCount function Counter() { const [count, setCount] = React.useState() if (typeof prevSetCount !== 'undefined') { console.log(setCount === prevSetCount) } prevSetCount = setCount return ( <button type="button" onClick={() => { setCount(count => count + 1) }}> Increment </button> ) } 
Clique no botão várias vezes e veja o console:
 true true true 
Você notará que 
setCount a mesma função para cada renderização. Isso também se aplica à nossa função de 
dispatch .
Isso significa que nossa função 
fetchDadJoke não depende de nada que muda ao longo do tempo e não depende de outros geradores de ação; portanto, o objeto de ação precisa ser criado apenas uma vez, na primeira renderização:
 const actions = React.useMemo(() => ({ fetchDadJoke, }), []) 
Agora que temos um objeto em cache com ações, podemos otimizar o valor do contexto? Na verdade, não, porque, por melhor que otimizemos o objeto de valor, ainda precisamos criar um novo a cada vez, devido a alterações de estado. No entanto, e se movermos um objeto de ação de um contexto existente para um novo? Quem disse que podemos ter apenas um contexto?
 const DadJokeStateContext = React.createContext() const DadJokeActionsContext = React.createContext() 
Podemos combinar os dois contextos em nosso 
DadJokeProvider :
  return ( <DadJokeStateContext.Provider value={state}> <DadJokeActionsContext.Provider value={actions}> {children} </DadJokeActionsContext.Provider> </DadJokeStateContext.Provider> ) 
E ajustar nossos ganchos:
 export function useDadJokeState() { return React.useContext(DadJokeStateContext) } export function useDadJokeActions() { return React.useContext(DadJokeActionsContext) } 
E nós terminamos! Sério, baixe quantas piadas quiser e veja por si mesmo.
 App: 1 Button: 1 DadJoke: 1 DadJoke: 2 DadJoke: 3 DadJoke: 4 DadJoke: 5 
Então você implementou sua própria solução otimizada de gerenciamento de estado! Você pode criar provedores diferentes usando esse modelo de dois contextos para criar seu aplicativo, mas isso não é tudo, você também pode renderizar o mesmo componente de provedor várias vezes! O que ?! Sim, tente a renderização 
DadJokeProvider em vários lugares e veja como a implementação de gerenciamento de estado é dimensionada facilmente!
Liberte sua imaginação e revise por que você realmente precisa do 
Redux .
Agradecimentos a Kent C. Dodds pelos artigos sobre o modelo de dois contextos. Eu nunca o vi em lugar nenhum e parece-me que isso muda as regras do jogo.
Leia as seguintes postagens do blog Kent para obter mais informações sobre os conceitos de que falei:
Quando usar useMemo e useCallbackComo otimizar o valor do contextoComo usar o React Context de maneira eficazGerenciando o estado do aplicativo no React.Um truque simples para otimizar as renderizações no React