Context提供了一种不需要手动地通过props来层层传递的方式来传递数据。
正文
在典型的React应用中,数据是通过props,自上而下地传递给子组件的。但是对于被大量组件使用的固定类型的数据(比如说,本地的语言环境,UI主题等)来说,这么做就显得十分的累赘和笨拙。Context提供了一种在组件之间(上下层级关系的组件)共享这种类型数据的方式。这种方式不需要你手动地,显式地通过props将数据层层传递下去。
什么时候用Context?
这一小节,讲的是context适用的业务场景。
Context是为那些可以认定为【整颗组件树范围内可以共用的数据】而设计的。比如说,当前已认证的用户数据,UI主题数据,当前用户的偏好语言设置数据等。举个例子,下面的代码中,为了装饰Button component我们手动地将一个叫“theme”的prop层层传递下去。 传递路径是:App -> Toolbar -> ThemedButton -> 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,我们可以跳过层层传递所经过的中间组件。现在我们的传递路径是这样的:App -> Button。
// 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} />;
}
}
在你用Context之前
这一小节,讲的是我们要慎用context。在用context之前,我们得考虑一下当前的业务场景有没有第二种技术方案可用。只有在确实想不出来了,才去使用context。
Context主要用于这种业务场景:大量处在组件树不同层级的组件需要共享某些数据。实际开发中,我们对context要常怀敬畏之心,谨慎使用。因为它犹如潘多拉的盒子,一旦打开了,就造成很多难以控制的现象(在这里特指,context一旦滥用了,就会造成很多组件难以复用)。
如果你只是单纯想免去数据层层传递时对中间层组件的影响,那么组件组合是一个相比context更加简单的技术方案。
举个例子来说,假如我们有一个叫Page
的组件,它需要将user
和avatarSize
这两个prop传递到下面好几层的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>
我们大费周章地将user
和avatarSize
这两个prop传递下去,最终只有Avatar
组件才真正地用到它。这种做法显得有点低效和多余的。假如,到后面Avatar
组件需要从顶层组件再获取一些格外的数据的话,你还得手动地,逐层地将这些数据用prop的形式来传递下去。实话说,这真的很烦人。
不考虑使用context的前提下,另外一种可以解决这种问题的技术方案是:将Avatar<