Контекстный API Реакции | Кодементор
Первоначально опубликовано на maximivanov.com
В React данные приложения и обработчики событий передаются дочерним компонентам через свойства. Но иногда нужно сделать некоторые данные доступными сразу на нескольких уровнях.
Передача данных через свойства вручную может быть громоздкой.
Чтобы решить эту проблему, React предоставляет контекстный API.
Вы создаете контекст, используя React.createContext(). Этот метод возвращает объект контекста:
{
Provider, Consumer;
}
Проще говоря, это два компонента Provider и Consumer. Вы передаете данные в Provider, а затем можете получить к ним доступ из подключенного Consumer.
контекст API
Вот простой пример.
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, когда у вас есть некоторые данные и, возможно, обратные вызовы, которые вам нужно разделить между несколькими компонентами, но вам придется передавать их через уровни компонентов, которым не нужны эти данные или обратные вызовы.