react 高级指引之Context

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性.

Context 的使用场景:
Context 设计目的是为共享那些被认为对于一个组件树而言是 "全局"的数据.例如当前ti认证的用户,主题或或首选语言.

function ThemedButton(props) {
	return <Button theme={props.theme} />;
}
//中间组件
function Toolbar(props) {
	//Toolbar 组件必须添加一个额外的 theme 属性
	// 然后传递他给 themedButton 组件
	return (
		<div>
			<ThemedButton theme={props.theme} />
		</div>
	);
}
class App extends React.Component {
	render() {
		return <Toolbar theme="dark" />;
	}
}

使用 context , 可以避免通过中间元素传递 props:

//创建一个 theme Context , 默认 theme 的值为 light
const ThemeContext = React.createContext('light');
function ThemedButton (props) {
	//ThemedButton 组件从 context 接收 theme 
	return (
		<ThemeContext.Consumer>
			{ theme => <Button { ...props } theme={theme} /> }
		<ThemeContext.Consumer>
	);
}
//中间组件
function Toolbar(props) {
	return (
		<div>
			<ThemedButton />
		</div>
	);
}
class App extends React.Component {
	render() {
		return (
			<ThemeContext.Provider value="dark">
				<Toolbar />
			</ThemeContext.Provider>
		)
	}
}

注意: 你要仅仅为了避免在几个层级下的组件传递 props 而使用 context . 他是被用于在多个层级的多个组件需要访问相同数据的情景.

API
React.createContext:

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

创建一对 { Provider, Consumer }. 当 React 渲染context 组件 Consumer 时,他将从组件树的上层中最接近的匹配的 Provider 读取当前的 context 值.
如果上层的组件树没有一个匹配的 Provider , 而此时你需要渲染一个 Consumer 组件,那么你可以用到 defaultValue. 这有助于在不封装他们的情况下对组件进行测试.
Provider

<Provider value={ /* some value */ }>

react 组件允许 Consumers 订阅context 的改变
接收一个value 属性传递给 Provider 的后代 Consumers. 一个 Provider 可以联系到多个 Consumers. Providers 可以被嵌套以覆盖组件树内更深层次的值.
Consumer

<Consumer>
	{ value => /* render something based on the context value */ }
</Consumer>

一个可以订阅context 变化的 React 组件.
接收一个函数作为子节点.函数接收当前context 的值并返回一个 React 节点. 传递给函数的value 将等于组件树中上层 context 的最近的 Provider的 value属性. 如果context 没有Provider .那么value 参数将等于被传递给 createContext() 的 defaultValue.
每当 Provider 的值发送改变时, 作为 Provider 后代的所有 Consumers 都会重新渲染. 从 Provider 到其后代的Consumers 传播不受 shouldComponentUpdate方法约束,因此即使祖先组件退出更新时,后代Consumer 也会被更新.

在生命周期方法中访问 Context
在生命周期方法中从上下文访问值是一种相对常见的用例.而不是将上下文添加到每一个生命周期方法中,只需要将它作为一个 props 传递,然后像通常使用 props 一样去使用它:

class Button extends React.Component {
	componentDidMount() {
		// ThemeContext value is this.props.theme
	}
	componentDidUpdate(prevProps,prevState) {
		//Prevous ThemeContext value is prevProps.theme
		//New ThemeContext value is this.props.theme
	}
	render() {
		const { theme , children } = this.props;
		return (
			<button className={ theme ? 'dark' : 'light' }>
				{ children }
			</button>
		)
	}
}
export default props => (
	<ThemeContext.Consumer>
		{ theme => <Button {...props} theme={theme}> /}
	</ThemeContext.Consumer>
)

告诫:
因为 context 使用 reference indentity 确定何时重新渲染, 在 Consumer 中, 当一个 Provider 的父节点重新渲染的时候,有一些问题可能触发意外的渲染.例如下面的代码,所有的 Consumer 在 Provider 重新渲染时,每次都将重新渲染,因为一个新的对象总是被创建对应 Provider 里的 value .

class App extends React.Component {
	render () {
		return (
			<Provider value={{ something: 'something' }}>
				<Toolbar />
			</Provider>
		)
	}
}

为了防止这样,提升value 到父节点的state 里.

class App extends React.Component {
	constructor (props) {
		this.state = {
			value: { something: 'something' },
		};
	}
	render() {
		return (
			<Provider value={this.state.value}>
				<Toolbar />
			</Provider>
		)
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值