现在遇到这样一个场景,有一个顶层父组件(A),包裹着两个子组件(B1和B2),子组件又分别包裹着孙组件(B1里有C1,B2里有C2),需求是点击孙组件1(C1)里的按钮,让孙组件2(C2)里的num增加,组件结构如下
// 需要点击C1来触发C2的UI变动
<A>
<B1>
<C1 />
</B1>
<B2>
<C2 />
</B2>
</A>
场景
我先用最传统的方法 (props传递) 先分析一下,这个交互涉及到C1和C2组件的动作,而它们不是简单的父子关系,props传递的中转站就要寻找到它们共同的祖先组件(即A),先无脑写下
// 父组件
class A extends Component {
state = {
num: 0
};
handleClickA = e => {
this.setState(prevState => ({
num: prevState.num + 1
}));
};
render() {
return (
<div style={{ background: "gray" }}>
<p>父组件</p>
<B1 handleClickA={this.handleClickA} />
<B2 num={this.state.num} />
</div>
);
}
}
// 子组件1
class B1 extends Component {
render() {
return (
<div style={{ background: "yellow", width: "400px" }}>
<p>子组件1</p>
<C1 handleClickA={this.props.handleClickA} />
</div>
);
}
}
// 子组件2
class B2 extends Component {
render() {
return (
<div style={{ background: "yellow", width: "400px" }}>
<p>子组件2</p>
<C2 num={this.props.num} />
</div>
);
}
}
// 孙组件1
class C1 extends Component {
render() {
return (
<div style={{ background: "white", width: "200px" }}>
<p>孙组件1</p>
<button onClick={this.props.handleClickA}>点我</button>
</div>
);
}
}
// 孙组件2
class C2 extends Component {
render() {
return (
<div style={{ background: "pink", width: "200px" }}>
<p>孙组件2</p>
<p>num:{this.props.num}</p>
</div>
);
}
}
发现B1和B2作为中介传递props的组件,尽管它们没用到相应的props,但为了传递各自后代组件所需的props,只能也挂上去,平时需求简单,中间转一层还好,但当需求变复杂时也许会造成 props drilling,<B1 {...this.props} />可能稍微好点,但本质上中介还要动手
是时候让React16.3的Context API上场了,一些知名框架如react-redux、mobx-react、react-router都使用了这个特性, 简单来说context就是做了一个中间商,它包含着供应者(Provider)和消费者(Consumer),Provider里可任意嵌套Consumer,而且可以与Consumer直接沟通,这特性解决了我们层层传递数据的麻烦
import React, { createContext, Component } from "react"; // 首先createContext导入
...
const { Provider, Consumer } = createContext(); // 调用方法拿到供应者和消费者组件
...
// 父组件
class A extends Component {
state = {
num: 0
};
handleClickA = e => {
this.setState(prevState => ({
num: prevState.num + 1
}));
};
render() {
return (
<Provider
value={{
num: this.state.num,
handleClickA: this.handleClickA
}}
{/* value可以被任何Consumer访问到 */}
>
<div style={{ background: "gray" }}>
<p>父组件</p>
<B1 />
<B2 />
</div>
</Provider>
);
}
}
// 子组件1
class B1 extends Component {
render() {
return (
<div style={{ background: "yellow", width: "400px" }}>
<p>子组件1</p>
{/* 不用再传递无用props */}
<C1 />
</div>
);
}
}
// 子组件2
class B2 extends Component {
render() {
return (
<div style={{ background: "yellow", width: "400px" }}>
<p>子组件2</p>
{/* 不用再传递无用props */}
<C2 />
</div>
);
}
}
// 孙组件1
class C1 extends Component {
render() {
return (
<Consumer>
{/* Consumer内部用了render props的设计模式,所以也要用函数方法来写 */}
{context => (
<div style={{ background: "white", width: "200px" }}>
<p>孙组件1</p>
<button onClick={context.handleClickA}>点我</button>
</div>
)}
</Consumer>
);
}
}
// 孙组件2
class C2 extends Component {
render() {
return (
<Consumer>
{context => (
<div style={{ background: "pink", width: "200px" }}>
<p>孙组件2</p>
<p>num:{context.num}</p>
</div>
)}
</Consumer>
);
}
}
React 新Context API上手就是如此简单,但作为有所追求的人,并不能满足于此,因为实际应用场景可能不止一个顶层组件用到Context,也许有多个地方都有这个需求,用高阶组件是复用react代码的好方法,我将在下篇继续探讨如何利用高阶组件复用Context
网友评论