React 源码阅读 -4
上下文 Context
上下文 Context
提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性。
何时使用 Context
Context
旨在共享一个组件树内可被视为“全局”的数据,例如当前经过身份验证的用户,主题或首选语言等;
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
// Used to track how many concurrent renderers this context currently
// supports within in a single renderer. Such as parallel server rendering.
_threadCount: 0,
// These are circular
Provider: (null: any),
Consumer: (null: any),
};
例子:
class App extends React.Component {render() {return <Toolbar theme="dark" />;}
}
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,// 因为必须将这个值层层传递所有组件。function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {render() {return <Button theme={this.props.theme} />;
}
}
使用 context
, 我们可以避免通过中间元素传递 props
:
const ThemeContext = React.createContext('light');
class App extends React.Component {render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。// 无论多深,任何组件都能读取这个值。// 在这个例子中,我们将“dark”作为当前的值传递下去。function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {.
static contextType = ThemeContext;
render() {return <Button theme={this.context} />;
}
}
使用 Context
之前的考虑
1.Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合(component composition
)有时候是一个比 context 更好的解决方案。
<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
一种无需 context
的解决方案是将 Avatar
组件自身传递下去,因而中间组件无需知道 user
或者 avatarSize
等 props
:
function Page(props) {
const user = props.user;
const userLink = (<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
);
return <PageLayout userLink={userLink} />;
}
// 现在,我们有这样的组件:<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout userLink={...} />
// ... 渲染出 ...
<NavigationBar userLink={...} />
// ... 渲染出 ...
{props.userLink}
但是,有的时候在组件树中很多不同层级的组件需要访问同样的一批数据。Context
能让你将这些数据向组件树下所有的组件进行“广播”,所有的组件都能访问到这些数据,也能访问到后续的数据更新。使用 context 的通用的场景包括管理当前的 locale,theme
,或者一些缓存数据,这比替代方案要简单的多。
React.createContext
const MyContext = React.createContext(defaultValue);
创建一个 Context
对象。当 React
渲染一个订阅了这个 Context
对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider
中读取到当前的 context
值。
只有当组件所处的树中没有匹配到 Provider
时,其 defaultValue
参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined
传递给 Provider
的 value
时,consumer
组件的 defaultValue
不会生效
Context.Provider
每个 Context
对象都会返回一个 Provider React
组件,它允许 consumer
组件订阅 context
的变化。
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
Provider
接收一个 value
属性,传递给 consumer
组件。一个 Provider
可以和多个 consumer
组件有对应关系。多个 Provider
也可以嵌套使用,里层的会覆盖外层的数据。
当 Provider
的 value
值发生变化时,它内部的所有
consumer 组件都会重新渲染。Provider
及其内部 consumer
组件都不受制于 shouldComponentUpdate
函数,因此当 consumer
组件在其祖先组件退出更新的情况下也能更新。
通过新旧值检测来确定变化,使用了与 Object.is
相同的算法。
Class.contextType
class MyClass extends React.Component {componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
挂载在 class
上的 contextType
属性会被重赋值为一个由 React.createContext()
创建的 Context
对象。这能让你使用 this.context
来消费最近 Context
上的那个值。你可以在任何生命周期中访问到它,包括 render
函数中。
多个的情况
https://zh-hans.reactjs.org/d…
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}
Context.Consumer
const Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
calculateChangedBits: context._calculateChangedBits,
};
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染 */}
</MyContext.Consumer>
这里,React
组件也可以订阅到 context
变更。这能让你在函数式组件中完成订阅 context。
这需要函数作为子元素 (function as a child)
这种做法。这个函数接收当前的 context
值,返回一个 React
节点。传递给函数的 value
值等同于往上组件树离这个 context
最近的 Provider
提供的 value
值。如果没有对应的 Provider,value
参数等同于传递给 createContext()
的 defaultValue
。
http://react.html.cn/docs/con…