Artigo original: https://www.freecodecamp.org/news/react-context-for-beginners/

React Context é uma ferramenta essencial para todo desenvolvedor React conhecer. Ele permite compartilhar facilmente o estado em suas aplicações.

Neste guia, abordaremos o que é o React Context, como usá-lo, quando e quando não usar o context, e muito mais.

Mesmo que você nunca tenha trabalhado com o React context antes, está no lugar certo. Você aprenderá tudo o que precisa saber com exemplos simples e passo a passo.

Vamos começar!

Conteúdo

O que é o React Context?

React Context nos permite passar e usar (consumir) dados em qualquer componente que precisamos em nossa aplicação do React sem usar propriedades.

Em outras palavras, o React Context nos permite compartilhar dados (estados) entre nossos componentes de modo mais fácil.

Caso você não saiba o que são estados (states), esse é um termo que se refere a um objeto que armazena dados, que podem mudar ao longo do tempo. É uma maneira de gerenciar e atualizar dados em um componente ou aplicação sem afetar outras partes.

Quando devo usar o React Context?

O React Context é muito bom quando você está transmitindo dados que podem ser usados ​​em qualquer componente da aplicação.

Esses tipos de dados incluem:

  • Dados do tema (como modo claro ou escuro)
  • Dados do usuário (a autenticação do usuário)
  • Dados específicos do local (como idioma ou o local do usuário)

Os dados que devem ser colocados no React Context são aqueles que não precisam ser atualizados com frequência.

Por quê? Porque o Context não foi criado como um sistema completo de gerenciamento de estado. Ele foi feito para facilitar o consumo dos dados.

Você pode pensar no React Context como o equivalente a variáveis ​​globais para nossos componentes do React.

Quais problemas o React Context resolve?

O React Context nos ajuda a evitar o problema de propagação de propriedades (props drilling).

A propagação de propriedades (props drilling) é um termo usado para descrever quando você passa propriedades por vários níveis para um componente aninhado, através de componentes que não precisam delas.

Aqui está um exemplo de propagação de propriedades. Nesta aplicação, temos acesso aos dados do tema que queremos passar como propriedade para todos os componentes.

Como você pode ver, os filhos diretos de App, como o Header, também precisam passar os dados do tema usando propriedades.

export default function App({ theme }) {
  return (
    <>
      <Header theme={theme} />
      <Main theme={theme} />
      <Sidebar theme={theme} />
      <Footer theme={theme} />
    </>
  );
}

function Header({ theme }) {
  return (
    <>
      <User theme={theme} />
      <Login theme={theme} />
      <Menu theme={theme} />
    </>
  );
}

Qual é o problema com este exemplo?

O problema é que estamos passando o theme através de vários componentes que não precisam dele imediatamente.

O componente Header não precisa do theme além de repassá-lo para o seu componente filho. Em outras palavras, seria melhor para User, Login e Menu consumirem os dados de theme diretamente.

Essa é a vantagem do React Context – podemos evitar totalmente o uso de props e, assim, evitar o problema de "props drilling".

Como eu uso o React Context?

O Context é uma API integrada ao React, a partir da sua versão 16.

Isso significa que podemos criar um Context e usá-lo diretamente importando o React em qualquer projeto.

Existem quatro etapas para usar o React Context:

  1. Crie o context usando o método createContext
  2. Pegue o context criado e envolva a árvore de componentes com o provedor (provider) do context.
  3. Coloque qualquer valor que desejar no provedor do React Context usando a propriedade value.
  4. Leia esse valor em qualquer componente usando o consumidor do Context.

Tudo isso parece meio confuso? É mais simples do que você pensa.

Vamos dar uma olhada em um exemplo muito básico. No nosso App, vamos passar o nosso próprio nome usando o Context e lê-lo em um componente aninhado: User.

import React from 'react';

export const UserContext = React.createContext();

export default function App() {
  return (
    <UserContext.Provider value="Reed">
      <User />
    </UserContext.Provider>
  )
}

function User() {
  return (
    <UserContext.Consumer>
      {value => <h1>{value}</h1>} 
      {/* prints: Reed */}
    </UserContext.Consumer>
  )
}

Vamos explicar o passo a passo do que estamos fazendo:

  1. Acima do nosso componente App, estamos criando um context com React.createContext() e colocando o resultado em uma variável, UserContext. Geralmente, você vai querer exportá-lo, como estamos fazendo aqui, porque seu componente estará em outro arquivo. Note que podemos passar um valor inicial para a propriedade value quando chamamos React.createContext().
  2. No nosso componente App, estamos usando UserContext – especificamente, UserContext.Provider. Ele é um objeto com duas propriedades: Provider e Consumer – ambas são componentes. Para passar nosso valor para todos os componentes em nosso App, colocamos nosso componente Provider ao redor deles (neste caso, User).
  3. No UserContext.Provider, colocamos o valor que queremos passar para toda a árvore de componentes. Igualamos à propriedade value para fazer isso. Neste caso, é o nosso nome.
  4. No componente User, ou onde queremos consumir (ou usar) o que foi fornecido em nosso contexto, usamos o componente do consumidor: UserContext.Consumer. Para usar nosso valor passado, usamos o que é chamado de padrão de renderização dessas props. É apenas uma função que o componente do consumidor nos dá como sendo uma propriedade. No retorno dessa função, podemos retornar e usar o value.

O que é o hook useContext?

Olhando o exemplo acima, o padrão de renderização de props para usar Context pode parecer um pouco estranho para você.

Outro modo de usá-lo foi disponibilizado no React 16.8 com a chegada dos hooks do React. Agora, podemos usar o Context com o hook useContext.

Em vez de usar as renderizações de propriedades, podemos passar todo o objeto de Context para React.useContext() consumir o contexto na parte superior do nosso componente.

Aqui está o exemplo usando o hook useContext:

import React from 'react';

export const UserContext = React.createContext();

export default function App() {
  return (
    <UserContext.Provider value="Reed">
      <User />
    </UserContext.Provider>
  )
}

function User() {
  const value = React.useContext(UserContext);  
    
  return <h1>{value}</h1>;
}

O benefício do hook useContext é que ele torna nossos componentes mais concisos, permitindo criar nossos próprios hooks personalizados.

Você pode usar tanto o componente consumidor diretamente quanto o hook useContext, dependendo apenas do padrão de sua preferência.

Você pode não precisar do Context

O erro que muitos desenvolvedores cometem é o de utilizar context quando eles precisam passar propriedades entre vários níveis de componente.

Aqui está uma aplicação com um componente Avatar que requer duas propriedades, username e avatarSrc, do componente App.

export default function App({ user }) {
  const { username, avatarSrc } = user;

  return (
    <main>
      <Navbar username={username} avatarSrc={avatarSrc} />
    </main>
  );
}

function Navbar({ username, avatarSrc }) {
  return (
    <nav>
      <Avatar username={username} avatarSrc={avatarSrc} />
    </nav>
  );
}

function Avatar({ username, avatarSrc }) {
  return <img src={avatarSrc} alt={username} />;
}

Se for possível, queremos evitar passar várias propriedades pelos componentes que não precisam delas.

O que podemos fazer?

Em vez de recorrer imediatamente ao React Context, por estarmos passando muitas propriedades, devemos compor melhor nossos componentes.

Como apenas o componente mais alto, App, precisa saber sobre o componente Avatar, podemos criá-lo diretamente dentro de App.

Isso nos permite passar apenas uma propriedade, avatar, ao invés de duas.

export default function App({ user }) {
  const { username, avatarSrc } = user;

  const avatar = <img src={avatarSrc} alt={username} />;

  return (
    <main>
      <Navbar avatar={avatar} />
    </main>
  );
}

function Navbar({ avatar }) {
  return <nav>{avatar}</nav>;
}

Resumindo: não utilize imediatamente o Context. Veja se é possível organizar melhor seus componentes para evitar a propagação excessiva de propriedades.

Ele substitui o Redux?

Sim e não.

Para muitos iniciantes em React, o Redux é um modo mais fácil de passar dados entre componentes. Isso ocorre porque o Redux já vem com o React Context.

Contudo, se você não está atualizando o estado, mas apenas passando-o pela árvore de componentes, você não precisa de uma biblioteca global de gerenciamento de estado como o Redux.

Avisos sobre o React Context

Por que não é possível atualizar o valor que o React Context repassa?

Mesmo sendo possível combinar React Context com hooks, como useReducer, e criar uma biblioteca improvisada de gerenciamento de estado sem utilizar nenhuma biblioteca de terceiros, geralmente não é recomendado por motivos de desempenho.

O problema com isso está no modo como o React Context desencadeia uma nova renderização.

Se você estiver repassando um objeto no provedor do React Context e qualquer propriedade desse objeto for atualizada, o que acontece? Qualquer componente que o use será renderizado novamente.

Isso pode não gerar um problema de desempenho em aplicações menores com poucos valores de estado que não são atualizados com muita frequência (como dados de tema). Porém, é um problema se você estiver realizando muitas atualizações de estado em uma aplicação com muitos componentes em sua árvore de componentes.

Consideração final

Espero que este guia tenha proporcionado a você uma melhor compreensão de como usar o React Context do início ao fim.

Se você deseja entender melhor como usar o React context para construir projetos incríveis com o React, confira o The React Bootcamp (curso criado pelo autor, em inglês).