Context 通過(guò)組件樹(shù)提供了一個(gè)傳遞數(shù)據(jù)的方法,從而避免了在每一個(gè)層級(jí)手動(dòng)的傳遞 props 屬性。
在一個(gè)典型的 React 應(yīng)用中贴汪,數(shù)據(jù)是通過(guò) props 屬性由上向下(由父及子)的進(jìn)行傳遞的,但這對(duì)于某些類(lèi)型的屬性而言是極其繁瑣的(例如:地區(qū)偏好,UI主題)瞧柔,這是應(yīng)用程序中許多組件都所需要的。 Context 提供了一種在組件之間共享此類(lèi)值的方式睦裳,而不必通過(guò)組件樹(shù)的每個(gè)層級(jí)顯式地傳遞 props 造锅。
Context 設(shè)計(jì)目的是為共享那些被認(rèn)為對(duì)于一個(gè)組件樹(shù)而言是“全局”的數(shù)據(jù)
是不是可以理解為:Context是一個(gè)局部的全局變量
來(lái)看一個(gè)例子
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,必須要一層層的傳下去
再來(lái)看看如果使用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包裹住想要獲取值的組件廉邑,然后用函數(shù)的方式傳值
<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里設(shè)置初始值
<ThemeContext.Provider value='99'>
return <F1 />
</ThemeContext.Provider>
</div>
)
}
}
來(lái)看看babel編譯以后的樣子就更容易理解了
來(lái)對(duì)這塊做個(gè)解析
<ThemeContext.Consumer>
{(n) => <F4 n4={n} />}
</ThemeContext.Consumer>
我們?cè)囍鴣?lái)還原一下這個(gè)結(jié)構(gòu)
function F1(){
console.log('我被調(diào)用了')
return 'F1'
}
function Abc(props){
//可以直接打印{F1}的值
console.log(props.children)
//也可以直接調(diào)用F1
props.children()
return (
<div>
{props.x}
{props.children} //通過(guò)這個(gè)可以拿到{F1}
</div>
)
}
function App(){
return (
<Abc x='Abc'>
{F1}
</Abc>
)
}
在Abc
里調(diào)用F1
時(shí)哥蔚,可以給F1
傳值
function Abc(props){
let x = 100
//調(diào)用F1時(shí)給它傳值
props.children(x)
return (
<div>
{props.x}
{props.children} //通過(guò)這個(gè)可以拿到{F1}
</div>
)
}
function App(){
return (
<Abc x='Abc'>
//直接使用箭頭函數(shù)
{(n)=>console.log('我得到的信息是',n)}
</Abc>
)
}
現(xiàn)在就很清楚了,Abc
就是ThemeContext.Consumer
function ThemeContext.Consumer(props){
let x = 100
//調(diào)用F1時(shí)給它傳值,并且可以得到F1的返回值
let result = props.children(x)
return (
<div>
{props.children} //通過(guò)這個(gè)可以拿到{F1}
</div>
)
}
function App(){
return (
<ThemeContext.Consumer >
//直接使用箭頭函數(shù),把F1里的內(nèi)容換成標(biāo)簽,
{(n)=><div>{n}</div>}
</ThemeContext.Consumer>
)
}
這樣就解釋了ThemeContext.Consumer
是什么東西----一個(gè)接受函數(shù)的組件
再回過(guò)頭來(lái)看
<ThemeContext.Provider value='99'>
return <F1 />
</ThemeContext.Provider>
ThemeContext.Provider
的value
不光能傳普通的數(shù)字鬓催、字符串肺素,也可以傳函數(shù)、對(duì)象
來(lái)實(shí)現(xiàn)一個(gè)通過(guò)ThemeContext.Provider
讓孫元素調(diào)用改變值的列子
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, //拓展運(yùn)算符宇驾,保留之前的this.state.x
n : this.state.x.n + 1
}
})
}
}
}
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state.x}>
return <F1 />
</ThemeContext.Provider>
</div>
)
}
}