No React, o contexto é uma maneira de passar dados entre árvores de componentes sem adicionar adereços manualmente para cada camada de componentes. O contexto fornece uma maneira de compartilhar valores especificados entre componentes sem ter que passá-los explicitamente através do componente. na árvore.
O ambiente operacional deste tutorial: sistema Windows 10, react versão 17.0.1, computador Dell G3.
O contexto fornece uma maneira de passar dados entre árvores de componentes sem adicionar manualmente adereços a cada camada de componentes. Em um aplicativo React típico, os dados são passados de cima para baixo (de pai para filho) por meio de adereços, mas essa abordagem é extremamente complicada para certos tipos de propriedades (por exemplo, preferências de localidade, tema de UI), essas propriedades são exigidas por muitos componentes em o aplicativo. O contexto fornece uma maneira de compartilhar esses valores entre os componentes sem precisar passar explicitamente os adereços por cada nível da árvore de componentes.
O contexto foi projetado para compartilhar dados "globais" com uma árvore de componentes, como o usuário, tema ou idioma preferencial atualmente autenticado. Por exemplo, no código a seguir, ajustamos manualmente o estilo de um componente de botão por meio de um atributo "tema"
class App extends React.Component { render() { return <Toolbar theme="dark" />; function Toolbar(props) { // O componente Toolbar aceita uma propriedade "tema" adicional, que é então passada para o ThemedButton componente. // Seria problemático se cada botão do aplicativo precisasse saber o valor do tema, // porque esse valor teria que ser transmitido a todos os componentes. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton estende React.Component { render() { return <Button theme={this.props.theme} /> }; }// Passar pelos adereços: App -> Barra de ferramentas -> ThemedButton// Se o aninhamento for muito profundo, então os adereços precisam ser passados camada por camada, mesmo que os adereços não sejam necessários no meio, parece muito complicado.Usando o contexto, podemos evitar a passagem de adereços por elementos intermediários
// O contexto nos permite passar valores profundamente na árvore de componentes sem precisar passá-los explicitamente por cada componente. // Cria um contexto para o tema atual ("light" é o valor padrão). const ThemeContext = React.createContext('light');class App extends React.Component { render() { // Use um provedor para passar o tema atual para a árvore de componentes a seguir. // Não importa a profundidade, qualquer componente pode ler este valor. // Neste exemplo, passamos "dark" como valor atual. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> }}// O componente intermediário não precisa mais especificar o tema a ser transmitido. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton extends React.Component { // Especifique contextType para ler o contexto do tema atual. // O React encontrará o provedor de tema mais próximo e usará seu valor. // Neste exemplo, o valor do tema atual é "dark". static contextType = ThemeContext; render() { return <Button theme={this.context} /> }}// Você também pode usar ThemedButto.contextType = ThemeContext;Crie um objeto Contexto. Quando o React renderiza um componente inscrito neste objeto Context, o componente lerá o valor do contexto atual do Provider correspondente mais próximo de si na árvore de componentes.
Somente quando não houver nenhum Provider correspondente na árvore onde o componente está localizado, seu parâmetro defaultValue terá efeito. Isso ajuda a testar componentes sem usar um provedor para encapsular o componente. Nota: Quando indefinido é passado para o valor do Provedor, o defaultValue do componente de consumo não terá efeito.
Cada objeto Context retorna um componente Provider React, que permite consumir componentes para assinar alterações de contexto.
O provedor recebe um atributo de valor e o passa para o componente consumidor. Um Provedor pode ter um relacionamento correspondente com vários componentes do consumidor. Vários provedores também podem ser usados aninhados e a camada interna substituirá os dados da camada externa.
Quando o valor do Provedor for alterado, todos os componentes consumidos dentro dele serão renderizados novamente. Nem o Provedor nem seus componentes internos do consumidor estão sujeitos à função shouldComponentUpdate, portanto, o componente consumidor pode ser atualizado mesmo se seu componente ancestral sair da atualização.
O atributo contextType montado na classe será reatribuído a um objeto Context criado por React.createContext(). Isso permite que você use this.context para consumir o valor no contexto mais recente. Você pode acessá-lo em qualquer ciclo de vida, incluindo a função de renderização
import MyContext from './MyContext';class MyClass extends React.Component { componentDidMount() { let value = this.context /* Depois que o componente for montado, use o valor do componente MyContext para executar algumas operações de efeito colateral* / } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; ; /* Renderizar com base no valor do componente MyContext*/ } // Ou use static contextType = MyContext;}MyClass.contextType = MyContext;Aqui, os componentes do React também podem assinar mudanças de contexto. Isso permite que você assine o contexto em componentes funcionais.
Isso requer funcionar como uma criança. Esta função recebe o valor do contexto atual e retorna um nó React. O valor passado para a função é equivalente ao valor fornecido pelo Provedor mais próximo deste contexto na árvore de componentes. Se não houver um Provider correspondente, o parâmetro value será equivalente ao defaultValue passado para createContext().
O objeto de contexto aceita uma propriedade chamada displayName, que é do tipo string. React DevTools usa essa string para determinar qual contexto exibir.
O seguinte componente aparecerá como MyDisplayName no DevTools
const MyContext = React.createContext(/* algum valor */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" em DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" Em Ferramentas de desenvolvimentoPara o exemplo do tema acima, um uso mais complexo pode ser alcançado usando valores dinâmicos.
tema-context.js
exportar const temas = { claro: { primeiro plano: '#000000', fundo: '#eeeeee', }, escuro: { primeiro plano: '#ffffff', fundo: '#222222', },}; exportar const ThemeContext = React .createContext(themes.dark); // Este é o valor padrãobotão temático.js
import { ThemeContext } from './theme-context'; class ThemedButton estende React.Component { render() { let props = this.props; // Obtenha o valor padrão em ThemeContext let theme = this.context return ( < button; {...props} style={{backgroundColor: theme.background}} /> }); // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;export default ThemedButton;aplicativo.js
import { ThemeContext, temas } from './theme-context';import ThemedButton from './themed-button';// Uma função de componente intermediário usando ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > Alterar tema </ThemedButton> );} class App estende React.Component { construtor(props) { super(props); .setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, })); value no estado, // e componentes externos usam o tema padrão value return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> }}ReactDOM.render(<App />, document.root);// Componentes agrupados por ThemeContext.Provider podem consumir ThemeContext O valor // na barra de ferramentas e ThemedButton podem usar this.context para obter o valor // Observe que o método de atualização do estado é transmiti-lo por meio de adereços, e a atualização é acionada por componentes descendentes. O método por meio do contexto será discutido abaixo.No exemplo acima, passamos uma função de atualização por meio de adereços para alterar o valor dos temas no aplicativo. Sabemos que é necessário atualizar o contexto de um componente que está profundamente aninhado na árvore de componentes. Nesse cenário, você pode passar uma função pelo contexto para fazer com que o componente de consumo atualize o contexto.
tema-context.js
// Certifique-se de que a estrutura de dados de valor padrão passada para createContext corresponda aos componentes de chamada (consumidores)! export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {}, // Defina o método para atualizar o tema, transmita-o});theme-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // O botão Theme Toggler não apenas obtém o valor do tema, mas também obtém uma função toggleTheme do contexto (parte app.js abaixo) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Alternar tema </button> )} </ThemeContext.Consumer> ); }exportar ThemeTogglerButton padrão;aplicativo.js
importar { ThemeContext, temas } de './theme-context'; importar ThemeTogglerButton de './theme-toggler-button'; class App estende React.Component { construtor (props) { super (props); ) => { this.setState(state => ({ theme: state.theme === temas.dark ? temas.light : temas.dark, })); // State também contém a função de atualização, portanto, ele contém a função de atualização. Será passado para o provedor de contexto. this.state = { theme: themes.light, toggleTheme: this.toggleTheme, // Defina a função de atualização e transmita-a através do contexto } } render() { // Todo o estado é passado para o provedor return ( <; ThemeContext.Provedor value={this.state}> <Content /> </ThemeContext.Provider> }}function Content() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <Aplicativo />, document.root);Para garantir que o contexto seja renderizado novamente rapidamente, o React precisa tornar o contexto de cada componente consumidor um nó separado na árvore de componentes.
// Contexto do tema, o tema padrão é o valor "light" const ThemeContext = React.createContext('light'); // Contexto de login do usuário const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const {signedInUser, theme } = this.props; // Componente do aplicativo que fornece o valor de contexto inicial return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < Layout /> </UserContext.Provider> </ThemeContext.Provider> ); }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// Um componente pode consumir várias funções de contexto Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext .Consumidor> )} </ThemeContext.Consumer> );}Se dois ou mais valores de contexto são frequentemente usados juntos, você pode considerar a criação de seu próprio componente de renderização para fornecer esses valores.
Como o contexto usa uma identidade de referência para decidir quando renderizar, pode haver algumas armadilhas que podem desencadear uma renderização inesperada no componente de consumidores quando o componente pai do provedor for renderizado novamente. Por exemplo, toda vez que o Provedor for renderizado novamente, o código a seguir renderizará novamente todos os componentes do consumidor a seguir, porque o atributo value é sempre atribuído a um novo objeto
class App estende React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }});Para evitar esta situação, o estado do valor é promovido para o estado do nó pai.
class App extends React.Component { constructor(props) { super(props); // Após múltiplas renderizações, o estado será retido. Quando o valor não mudar, os seguintes componentes consumidores não serão renderizados novamente this.state = {. valor: {alguma coisa: 'alguma coisa'}, }; } render() { return ( <Provider value={this.state.value}> <Barra de ferramentas /> </Provider> }});