контекст
Контекст обеспечивает способ передачи данных через дерево компонентов без необходимости вручную пропускать подпорки на каждом уровне.
В типичном приложении React данные передаются сверху вниз (от родителя к потомку) через реквизиты, но это может быть затруднительно для определенных типов реквизитов (например, предпочтения языкового стандарта, темы пользовательского интерфейса), которые требуются для многих компонентов в приложении. Контекст обеспечивает способ обмена такими значениями между компонентами, без необходимости явно передавать реквизит через каждый уровень дерева.
Когда использовать контекст
Контекст предназначен для совместного использования данных, которые можно считать «глобальными» для дерева компонентов React, таких как текущий аутентифицированный пользователь, тема или предпочитаемый язык. Например, в приведенном ниже коде мы вручную пропустили пропеллер «theme» для стилизации компонента 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} />;
}
}
Используя контекст, мы можем избежать передачи реквизита через промежуточные элементы:
// 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(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} />;
}
}
Прежде чем использовать контекст
Контекст в основном используется, когда некоторые данные должны быть доступны для многих компонентов на разных уровнях вложенности. Примените это экономно, потому что это делает повторное использование компонента более трудным.
Если вы хотите избежать пропуска некоторых элементов через множество уровней, состав компонентов часто является более простым решением, чем контекст.
Например, рассмотрим компонент Page, который пропускает пользователя и avatarSize на несколько уровней, чтобы его могли прочитать глубоко вложенные компоненты Link и Avatar:
<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>
Может показаться излишним передавать пользователя и реквизиты avatarSize через многие уровни, если в конце концов это действительно нужно только компоненту Avatar. Также раздражает, что всякий раз, когда компоненту «Аватар» требуется больше реквизита сверху, вы должны добавлять его также на всех промежуточных уровнях.
Один из способов решить эту проблему без контекста - передать сам компонент Avatar, чтобы промежуточным компонентам не нужно было знать о реквизитах пользователя или 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}
С этим изменением только самый верхний компонент Page должен знать об использовании компонентов Link и Avatar user и avatarSize.
Эта инверсия управления во многих случаях может сделать ваш код чище, уменьшив количество реквизитов, которые вам необходимо пройти через ваше приложение, и предоставив больший контроль корневым компонентам. Тем не менее, это не правильный выбор в каждом случае: перемещение большей сложности вверх по дереву делает эти компоненты более высокого уровня более сложными и заставляет компоненты более низкого уровня быть более гибкими, чем вы могли бы пожелать.
Вы не ограничены одним ребенком для компонента. Вы можете передать несколько детей или даже иметь несколько отдельных «слотов» для детей.
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}
/>
);
}
Эта схема достаточна для многих случаев, когда вам необходимо отделить ребенка от его непосредственных родителей. Вы можете пойти еще дальше с помощью реквизита рендеринга, если ребенку нужно связаться с родителем перед рендерингом.
Однако иногда одни и те же данные должны быть доступны для многих компонентов дерева и на разных уровнях вложенности. Контекст позволяет вам «транслировать» такие данные и вносить изменения в них для всех компонентов, указанных ниже. Типичные примеры, в которых использование контекста может быть проще, чем альтернативы, включают управление текущей локалью, темой или кешем данных.
Новый контент: Composer: менеджер зависимостей для PHP , R программирования