Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
在一个典型的 React 应用中,数据是通过 props 属性由上向下(由父及子)的进行传递的,但这对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI主题),这是应用程序中许多组件都所需要的。 Context 提供了一种在组件之间共享此类值的方式,而不必通过组件树的每个层级显式地传递 props 。
Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据
是不是可以理解为:Context是一个局部的全局变量
来看一个例子
function F1(props) {
return (
<div className="border">
1111,{props.n1}
<F2 n2={props.n1} />
</div>
);
}
function F2(props) {
return (
<div className="border">
2222,{props.n2}
<F3 n3={props.n2} />
</div>
);
}
function F3(props) {
return (
<div className="border">
3333,{props.n3}
<F4 n4={props.n3} />
</div>
);
}
function F4(props) {
return <div className="border">4444, {props.n4}</div>;
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
n: 99
};
}
render() {
return <F1 n1={this.state.n} />;
}
}
如果想在F4里获取到App里的n,必须要一层层的传下去
再来看看如果使用Context该怎么做
function F1() {
return (
<div className="border">
1111
<F2/>
</div>
);
}
function F2() {
return (
<div className="border">
2222
<F3/>
</div>
);
}
function F3() {
return (
<div className="border">
3333
//必须用ThemeContext.Consumer包裹住想要获取值的组件,然后用函数的方式传值
<ThemeContext.Consumer>
{(n) => <F4 n4={n} />}
</ThemeContext.Consumer>
</div>
);
}
function F4(props) {
return (
<div className="border">4444, {props.n4}</div>
)
}
//声明ThemeContext
const ThemeContext = React.createContext();
class App extends React.Component {
render() {
return (
<div>
//必须用ThemeContext.Provider包裹外层
//在value里设置初始值
<ThemeContext.Provider value='99'>
return <F1 />
</ThemeContext.Provider>
</div>
)
}
}
来看看babel编译以后的样子就更容易理解了
来对这块做个解析
<ThemeContext.Consumer>
{(n) => <F4 n4={n} />}
</ThemeContext.Consumer>
我们试着来还原一下这个结构
function F1(){
console.log('我被调用了')
return 'F1'
}
function Abc(props){
//可以直接打印{F1}的值
console.log(props.children)
//也可以直接调用F1
props.children()
return (
<div>
{props.x}
{props.children} //通过这个可以拿到{F1}
</div>
)
}
function App(){
return (
<Abc x='Abc'>
{F1}
</Abc>
)
}
在Abc
里调用F1
时,可以给F1
传值
function Abc(props){
let x = 100
//调用F1时给它传值
props.children(x)
return (
<div>
{props.x}
{props.children} //通过这个可以拿到{F1}
</div>
)
}
function App(){
return (
<Abc x='Abc'>
//直接使用箭头函数
{(n)=>console.log('我得到的信息是',n)}
</Abc>
)
}
现在就很清楚了,Abc
就是ThemeContext.Consumer
function ThemeContext.Consumer(props){
let x = 100
//调用F1时给它传值,并且可以得到F1的返回值
let result = props.children(x)
return (
<div>
{props.children} //通过这个可以拿到{F1}
</div>
)
}
function App(){
return (
<ThemeContext.Consumer >
//直接使用箭头函数,把F1里的内容换成标签,
{(n)=><div>{n}</div>}
</ThemeContext.Consumer>
)
}
这样就解释了ThemeContext.Consumer
是什么东西----一个接受函数的组件
再回过头来看
<ThemeContext.Provider value='99'>
return <F1 />
</ThemeContext.Provider>
ThemeContext.Provider
的value
不光能传普通的数字、字符串,也可以传函数、对象
来实现一个通过ThemeContext.Provider
让孙元素调用改变值的列子
function F1() {
return (
<div className="border">
1111
<F2/>
</div>
);
}
function F2() {
return (
<div className="border">
2222
<F3/>
</div>
);
}
function F3() {
return (
<div className="border">
3333
<ThemeContext.Consumer>
{(x)=><F4 n4={x.n} setN={x.setN} />}
</ThemeContext.Consumer>
</div>
);
}
function F4(props) {
return (<div className='border'>
<div className="border">4444, {props.n4}</div>
<button onClick={props.setN}>Click me</button>
</div>
)
}
const ThemeContext = React.createContext();
class App extends React.Component {
constructor(props){
super()
this.state = {
x : {
n : 99,
setN : ()=>{
this.setState({
x : {
...this.state.x, //拓展运算符,保留之前的this.state.x
n : this.state.x.n + 1
}
})
}
}
}
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state.x}>
return <F1 />
</ThemeContext.Provider>
</div>
)
}
}