Контекстный API Реакции | Кодементор

Первоначально опубликовано на maximivanov.com

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

Передача данных через свойства вручную может быть громоздкой.

Чтобы решить эту проблему, React предоставляет контекстный API.

Вы создаете контекст, используя React.createContext(). Этот метод возвращает объект контекста:

{
  Provider, Consumer;
}

Проще говоря, это два компонента Provider и Consumer. Вы передаете данные в Provider, а затем можете получить к ним доступ из подключенного Consumer.
контекст API

порталы.png

Вот простой пример.

import React, { createContext } from "react";

const { Provider, Consumer } = createContext();


const App = () => (
  <Provider value={{ valuePassedThroughContext: "Success!" }}>
    <ChildComponent />
  </Provider>
);



const ChildComponent = () => (
  <Consumer>
    {value => {
      // Consumer requires a function as a child
      // and passes the `value` from Provider
      // down to it.
    }}
  </Consumer>
);

Мы можем использовать Consumer на любом уровне вложенности.

Он будет получать данные от своего провайдера в любом месте вниз по дереву.

Одно важное замечание здесь, что Consumer будет получать данные только из Provider он был создан с.

Передача значения по умолчанию

Вы можете передать значение по умолчанию в React.createContext:

const { Provider, Consumer } = React.createContext(defaultValue);

Это повлияет только на потребителя и только если у него не будет соответствия Provider в любом месте над ним в дереве.

В этом случае значение по умолчанию будет передано Consumer функция:

<Consumer>
  {defaultValue => {
    // some code
  }}
</Consumer>

Динамические значения

Все потребители, являющиеся потомками Provider будет перерисовываться всякий раз, когда поставщик value изменения реквизита.

Вот пример:

счетчик-контекст.jsx

import React, { createContext } from "react";

const { Provider, Consumer: CounterConsumer } = createContext(0);

class CounterProvider extends React.Component {
  state = { counter: 0 };

  componentDidMount() {
    this.tickInterval = setInterval(() => {
      this.setState({ counter: this.state.counter + 1 });
    }, 500);
  }

  componentWillUnmount() {
    clearInterval(this.tickInterval);
  }

  render() {
    <Provider value={this.state.counter}>{this.props.children}</Provider>;
  }
}

export { CounterProvider, CounterConsumer };

счетчик-view.jsx

import React, { Component } from "react";
import { CounterConsumer } from "./counter-context";

export const CounterView = () => (
  <CounterConsumer>{counter => <p>Current tick: {counter}</p>}</CounterConsumer>
);

app.js

import React, { Component } from "react";
import { CounterProvider, CounterConsumer } from "./counter-context";

class App extends Component {
  render() {
    return <>
      <CounterProvider>
        <CounterView>
      </CounterProvider>
      <CounterView/>
    </>;
  }
}

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

В этом примере первый CounterView будет обновляться при каждом тике.

Второй всегда будет показывать значение по умолчанию, потому что у него нет соответствующего Provider на дереве.

Передача функций

Вы также можете передавать функции через контекст. Это полезно, если вы хотите обновить Provider значение от потребители.

авторизация.jsx

import React, { Component, createContext } from "react";

const { Provider, Consumer: AuthConsumer } = createContext({ loggedIn: false });

class AuthProvider extends Component {
  state = { loggedIn: false };

  logIn = () => {
    this.setState({ loggedIn: true });
  };

  render() {
    return (
      <Provider value={{ loggedIn, logIn: this.logIn }}>
        {this.children}
      </Provider>
    );
  }
}

export { AuthProvider, AuthConsumer };

Здесь мы создали контекст и использовали присваивание деструктуризации создать две переменные Provider а также AuthConsumer.

Чтобы создать переменную AuthConsumer мы переименовали оригинал Consumer что мы собираемся из React.createContextметод.

Затем мы определили класс AuthProvider который управляет состоянием авторизации. Он окутывает детей Provider и проходит loggedIn государство и logIn функция к этому Provider.

приложение.jsx

import React from "react";
import { AuthProvider, AuthConsumer } from "./auth";

const App = () => (
  <AuthProvider>
    <Content />
  </AuthProvider>
);

const Content = () => {
  <AuthConsumer>
    {({ loggedIn, logIn }) =>
      loggedIn ? (
        <p>Congrats! You are logged in!</p>
      ) : (
        <button onClick={logIn}>Log in</button>
      )
    }
  </AuthConsumer>;
};

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

Можно использовать несколько контекстов одновременно.

Но вам придется использовать отдельного потребителя для каждого провайдера:

import React from "react";

const AuthContext = React.createContext({ loggedIn: false });

const ProfileContext = React.createContext({ name: "Tom" });

const App = () => (
  <AuthContext.Provider value={theme}>
    <UserContext.Provider value={signedInUser}>
      <Content />
    </UserContext.Provider>
  </AuthContext.Provider>
);

function Content() {
  return (
    <AuthContext.Consumer>
      {({ loggedIn }) => (
        <ProfileContext.Consumer>
          {user =>
            loggedIn ? (
              <p>Logged as: {user.name}</p>
            ) : (
              <p>You are not logged in</p>
            )
          }
        </ProfileContext.Consumer>
      )}
    </AuthContext.Consumer>
  );
}

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

Когда использовать контекст

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

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *