We want to hear from you!Take our 2020 Community Survey!

Context

Context fornisce un modo per passare i dati attraverso l’albero dei componenti senza dover passare manualmente i props ad ogni livello.

In un’applicazione tipica di React, i dati sono passati dall’alto verso basso (da genitore a figlio) tramite i props, ma questo può essere complicato per alcuni props (e.g. preferenza locale, tema interfaccia utente) che sono richiesti da molti componenti all’interno di un’applicazione. Context fornisce un modo per condividere valori come questi tra i componenti senza dover passare esplicitamente un prop attraverso ogni livello dell’albero.

Quando usare lo Context

Context é progettato per condividire i dati che possono essere considerati “globali” per un albero di componenti React, come l’utente autenticato corrente, tema, o lingua preferita. Per esempio, nel codice qui sotto manualmente infiliamo attraverso un prop “theme” per dare style al componente Button:

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // The Toolbar component must take an extra "theme" prop  // and pass it to the ThemedButton. This can become painful  // if every single button in the app needs to know the theme  // because it would have to be passed through all components.  return (
    <div>
      <ThemedButton theme={props.theme} />    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

Usando lo Context, possiamo evitare di passare i props attraverso elementi intermedi:

// Context lets us pass a value deep into the component tree// without explicitly threading it through every component.// Create a context for the current theme (with "light" as the default).const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the tree below.    // Any component can read it, no matter how deep it is.    // In this example, we're passing "dark" as the current value.    return (
      <ThemeContext.Provider value="dark">        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// A component in the middle doesn't have to// pass the theme down explicitly anymore.function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // Assign a contextType to read the current theme context.  // React will find the closest theme Provider above and use its value.  // In this example, the current theme is "dark".  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;  }
}

Prima di utilizzare lo Context

Context è principalmente utilizzato quando alcuni dati devono essere accessibili da molti componenti ai diversi livelli di nidificazione. Applicarlo con moderazione perché rende più difficile il riutilizzo dei componenti.

Se vuoi solo evitare di passare alcuni prop attraverso molti livelli, composizione del componente è spesso una soluzione più semplice dello context.

Per esempio, considera un componente Page che passa i props user e avatarSize diversi livelli verso il basso affichè i componenti Link e Avatar profondamente annidati lo possano leggere:

<Page user={user} avatarSize={avatarSize} />
// ... which renders ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... which renders ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... which renders ...
<Link href={user.permalink}>
  <Avatar user={user} size={avatarSize} />
</Link>

Potrebbe sembrare ridondante passare i props user e avatarSize attraverso molti livelli se alla fine solo il componente Avatar ne ha davvero bisogno. È anche fastidioso che ogni volta il componente Avatar ha bisogno di più props dall’alto, devi aggiungerli anche a tutti i livelli intermedi.

Un modo per risolvere questo problema senza lo context è di passare giù il componente Avatar stesso in modo che i componenti intermedi non debbano conoscere i props user o avatarSize:

function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// Now, we have:
<Page user={user} avatarSize={avatarSize} />
// ... which renders ...
<PageLayout userLink={...} />
// ... which renders ...
<NavigationBar userLink={...} />
// ... which renders ...
{props.userLink}

Con questo cambiamento, solo il componente Page più in alto deve conoscere l’utilizzo di user e avatarSize da componenti Link e Avatar.

Questa inversione del controllo può rendere il tuo codice più pulito in molti casi riducendo il numero di props che hai bisgno di passare attraverso la tua applicazione e fornendo un maggiore controllo ai componenti alle radici.

Tuttavia, questa non è la scelta giusta in ogni caso: spostare più complessità nell’albero verso alto rende più complicati quei componenti di alto livello e costringe i componenti di basso livello a essere più flessibili di quanto ne vorresti.

Non sei limitato a un singolo figlio per un componente. Potresti passare figli multipli, o anche avere più “slot” separati per i figli, come documentato qui:

function Page(props) {
  const user = props.user;
  const content = <Feed user={user} />;
  const topBar = (
    <NavigationBar>
      <Link href={user.permalink}>
        <Avatar user={user} size={props.avatarSize} />
      </Link>
    </NavigationBar>
  );
  return (
    <PageLayout
      topBar={topBar}
      content={content}
    />
  );
}

Questo modello è sufficiente per molti casi in cui è necessario disaccoppiare un figlio dai suoi genitori immediati.

Lo puoi portare ancora avanti con render props se il figlio ha bisogno di comunicare con il genitore prima di renderizzare.

Tuttavia, alcune volte i stessi dati devono essere accessbili da molti componenti nell’albero, ed ai diversi livelli di nidificazione. Lo context permette di “trasmettere” tale dati, e le modifiche ad essi , a tutte i componenti di seguito.

Esempi comuni dove context potrebbe essere più semplice delle alternative includono la gestione del locale corrente, del tema, o di una cache dei dati.

API

React.createContext

const MyContext = React.createContext(defaultValue);

Crea un oggetto Context. Quando React renderizza un componente che si iscrive a questo oggetto Context, esso leggerà il valore context corrente dal Provider corrispondente più vicino sopra di esso nell’albero.

L’argomento defaultValue è utilizzato soltanto quando un componente non ha un Provider corrispondente sopra di esso nell’albero. Questo può essere utile per testare i componenti in isolamento senza avvolgerli. Nota: passando undefined come valore del Provider non causa ai componenti consumer di utilizzare defaultValue

Context.Provider

<MyContext.Provider value={/* some value */}>

Ogni oggetto Context viene con un componente React Provider che consente ai componenti consumer di iscriversi alle modifiche dello context.

Il componente Provider accetta un props value da essere passato alle componenti consumer che sono discendenti di questo Provider. Uno Provider può essere connesso a consumer multipli. I Provider possono essere nidificati per sovrascrivere i valori più profondi all’interno dell’albero.

Tutti i consumer che sono discendenti di uno Provider si ri-renderizzeranno ogni volta il props value dello Provider cambia. La propagazione dallo Provider fino ai suoi consumer discendenti (includendo .contextType e useContext) non sono soggetti al metodo shouldComponentUpdate, quindi il consumer è aggiornato anche quando un componente antenato salta un aggiornamento.

Le modifiche sono determinate confrontando nuovi e vecchi valori usand lo stesso algoritmo di Object.is.

Nota

Il modo in cui vengono determinate le modifiche può causare alcuni problemi quando si passano oggetti come “valore”: vedere Avvertenze.

Class.contextType

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of MyContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of MyContext */
  }
}
MyClass.contextType = MyContext;

La proprietà contextType su una classe può essere assegnata un oggetto Context creato da React.createContext(). Ciò consente di consumare il valore corrente più vicino di quel tipo di Context usando this.context. È possibile fare riferimento a questo in uno qualsiasi dei metodi lifecycle, inclusa la funzione render.

Nota:

Puoi iscriverti solo aa uno singolo context utilizzando questa API. Se hai bisogno di leggere più di uno vedi Consumo di Più Context.

Se si utilizza la sintassi dei campi di classe pubblica sperimentale, puoi usare un campo di classe statico per inizializzare il tuo contextType

class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let value = this.context;
    /* render something based on the value */
  }
}

Context.Consumer

<MyContext.Consumer>
  {value =>  /* render something based on the context value */}
</MyContext.Consumer>

Un componente React che si iscrive alle modifiche. Ciò consente di iscriverti ad uno context dentro una componente funzione.

Richiede una funzione come un figlio. La funzione riceve il valore dello context corrente e restituisce un nodo React. L’argomento value passato alla funzione sarà uguale al prop value dello Provider per questo context sopra nell’albero. Se non c’è uno Provider per questo context sopra, l’argomento value sarà uguale a defaultValue che era stato passato a createContext().

Nota

Per ulteriori informazioni sul modello ‘funzione come figlio’, vedere render props.

Context.displayName

Oggetto Context accetta una proprietà stringa displayName. I React DevTools utilizzano questa stringa per determinare cosa visualizzare per lo context.

Per esempio, il seguente componente apparirà come MyDisplayName nei DevTools:

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" in DevTools

Esempi

Context Dinamico

Un esempio più complesso con valori dinamici per il tema:

theme-context.js

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(  themes.dark // default value);

themed-button.js

import {ThemeContext} from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;
export default ThemedButton;

app.js

import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';

// An intermediate component that uses the ThemedButton
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };
  }

  render() {
    // The ThemedButton button inside the ThemeProvider    // uses the theme from state while the one outside uses    // the default dark theme    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>          <Toolbar changeTheme={this.toggleTheme} />        </ThemeContext.Provider>        <Section>
          <ThemedButton />        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);

Aggiornamento dello Context da un Componente Nidificato

Spesso è necessario aggiornare il context da un componente che è nidificato profondamente da qualche parte nell’albero dei componenti. In questo caso è possibile passare una funzione attraverso lo context per consentire ai consumer di aggiornare il context:

theme-context.js

// Make sure the shape of the default value passed to
// createContext matches the shape that the consumers expect!
export const ThemeContext = React.createContext({
  theme: themes.dark,  toggleTheme: () => {},});

theme-toggler-button.js

import {ThemeContext} from './theme-context';

function ThemeTogglerButton() {
  // The Theme Toggler Button receives not only the theme  // but also a toggleTheme function from the context  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (        <button
          onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;

app.js

import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };

    // State also contains the updater function so it will    // be passed down into the context provider    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,    };
  }

  render() {
    // The entire state is passed to the provider    return (
      <ThemeContext.Provider value={this.state}>        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <div>
      <ThemeTogglerButton />
    </div>
  );
}

ReactDOM.render(<App />, document.root);

Consumo dei Context Multipli

Per mantenere veloce la ri-renderizzazione dello context, React deve rendere ogni consumer dello context un nodo separato nell’albero.

// Theme context, default to light theme
const ThemeContext = React.createContext('light');

// Signed-in user context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // App component that provides initial context values
    return (
      <ThemeContext.Provider value={theme}>        <UserContext.Provider value={signedInUser}>          <Layout />
        </UserContext.Provider>      </ThemeContext.Provider>    );
  }
}

function Layout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}

// A component may consume multiple contexts
function Content() {
  return (
    <ThemeContext.Consumer>      {theme => (        <UserContext.Consumer>          {user => (            <ProfilePage user={user} theme={theme} />          )}        </UserContext.Consumer>      )}    </ThemeContext.Consumer>  );
}

Se due o più valori dello context sono spesso utilizzati insieme, si potrebbe prendere in considerazione la creazione di un proprio componente render props che fornisce entrambi.

Avvertenze

Poiché lo context utilizza l’identità di riferimento per determinare quando ri-renderizzare, ci sono alcuni trabocchetti che potrebbero innescare renderizzazioni non intenzionali nei consumer quando il genitore di uno provider si ri-renderizza.

Per esempio, il codice sotto ri-renderizzerà tutti i consumer ogni volta che lo Provider si ri-renderizza perchè un nuovo oggetto è sempre creato per value:

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>        <Toolbar />
      </MyContext.Provider>
    );
  }
}

Per aggirare questo problema, sollevare il valore nello state del genitore:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},    };
  }

  render() {
    return (
      <Provider value={this.state.value}>        <Toolbar />
      </Provider>
    );
  }
}

API Legacy

Nota

React precedentemente fornito con un’API sperimentale dello context. La vecchia API sarà supportata in tutte le 16.x versioni, ma le applicazioni che lo utilizzano dovrebbero migrare alla nuova versione. L’API Legacy verrà rimossa in una versione futura principale di React. Leggi i docs dello context legacy.

Is this page useful?Edit this page