1. Context — инструмент React для доступа к данным между компонентами.

Node.js внешний интерфейс JavaScript

Серия "Раскрытие исходного кода React-router-dom"

2. Базовое использование React-Router
3. Демистификация исходного кода react-router-dom — BrowserRouter

ContextПредоставляет способ доступа к данным между компонентами. Ему не нужно передавать свойства слой за слоем между деревьями компонентов, а также он может легко получать доступ к данным других компонентов.

В классическом приложении React данные передаются от родительских компонентов к дочерним через свойства. Но в некоторых конкретных случаях некоторые данные должны быть разделены между различными компонентами.ContextПредоставьте нам способ обмена данными между компонентами, который может избежать передачи данных слой за слоем в дереве компонентов.

При использовании контекста

Context«Глобальные» данные могут совместно использоваться компонентами дерева компонентов. Например: информация о зарегистрированном пользователе, выбранная пользователем тема, язык и т. д. В приведенном ниже примере мы «вручную» передаем свойство темы сверху вниз, чтобы стилизовать кнопку.

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

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}></ThemedButton>
    </div>
  );
}

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

использоватьContext, мы можем избежать передачи реквизита через несколько промежуточных компонентов

// 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></Toolbar>
      </ThemeContext.Provider>
    );
  }
}

// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
  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} />;
  }
}

Иногда некоторым компонентам требуется доступ к некоторым данным, и эти компоненты находятся на разных уровнях дерева компонентов.ContextПозволяет нам «транслировать» изменения для обмена данными между компонентами.

API, связанный с контекстом

React.createContext

const MyContext = React.createContext(defaultValue);

создать новыйContextобъект. Когда React отображает компонент, и компонент зарегистрированContext, он будет читать родительский компонент, ближайший компонент Provider к компонентуContextстоимость

defaultValueТолькоОн будет использоваться только тогда, когда компонент «Потребитель» не может найти компонент «Поставщик».

Context.Provider

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

каждыйContextВсе объекты содержат компонент React, который называется Provider. Поставщик может заставить компонент «Потребитель» прослушивать изменения контекста.

Поставщик может установить контакт с несколькими компонентами-потребителями, передав свойство value потомкам компонентов-потребителей поставщика.

Все компоненты-потомки Consumer будут повторно визуализированы после обновления свойства value Provider. Это обновление распространяется от поставщика к его дочерним компонентам-потребителям, но не вызывает метод shouldComponentUpdate. Таким образом, даже если компонент-предок компонента-потребителя не обновляется, компонент-потребитель будет обновлен.

ContextИспользуйте тот же алгоритм, что и Object.is, чтобы сравнить новое и старое значения value, чтобы определить, было ли обновлено его значение.

Уведомление

Этот способ определения того, изменилось ли значение, может вызвать проблемы при передаче объекта в значение. пожалуйста, примите участиеCaveats.

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;

Присвоить значение свойству contextTpe классаContextПосле объекта мы можем получить текущую в каждой функции цикла объявления компонента через this.contextContextметоды объекта

Уведомление:

Таким образом, для каждого компонента может быть зарегистрирован только один объект контекста. Если вам нужно прочитать значение нескольких контекстов, примите участие вConsuming Multiple Contexts.

Если в кодировании используется синтаксис в эксперименте ES, для инициализации 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>

ConsumerЭто компонент React, который прослушивает изменения контекста. Это позволяет нам прослушивать изменения контекста в функциональном компоненте.

ConsumerКомпонент требует, чтобы его дочерний элемент был функцией. Параметр этой функции получает значение текущего контекста и требуется для возврата узла React (узла) Значение параметра, переданное этой функции, равно расстоянию от этогоConsumnerЗначение контекста ближайшего внешнего компонента Provider. Если внешний компонент Provider отсутствует, он равен значению параметра, переданному при вызове createContext() (значение контекста по умолчанию).

Уведомление

Для получения дополнительной информации о «подэлементе как функции» перейдите кrender props

каштан

Обновление контекста во вложенных компонентах

В процессе разработки нам часто нужно обновить значение контекста для некоторых глубоко вложенных компонентов. На этом этапе мы можем передать функцию и использовать ее для обновления значения контекста. код показывает, как показано ниже:

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);

Использование нескольких контекстов

Чтобы сохранить быстрый рендеринг React, нам нужно написать каждый потребительский компонент как независимый компонентный узел (узел).

// 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>
  );
}

Если есть более двух контекстов, которые часто используются вместе, нам нужно рассмотреть возможность создания компонента поддержки рендеринга, который предоставляет два контекста вместе.

Уведомление

Поскольку контекст использует ссылочное удостоверение, чтобы определить, когда выполнять повторную визуализацию, в некоторых случаях при повторной визуализации родительского элемента поставщика запускается невнутренняя визуализация потребителя. Например, следующий код будет повторно отображать все потребительские компоненты каждый раз при повторном отображении поставщика. Поскольку новый объект всегда будет создан и ему будет присвоено значение (значение постоянно меняется)

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

Чтобы избежать этой проблемы, вы можете поместить значение в состояние компонента

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

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