最近接到一個簡單的內部項目,邏輯並不複雜,就想着 不用redux了,用react的new context API試試看,折騰了兩天,把過程和感想跟你們分享下。react
因爲是公司的項目,因此下文的示例代碼都是我從新寫的,望見諒!git
本文使用的Demo:在這裏github
先讓咱們來看一下這個接口的基本用法: 使用它首先須要使用react的createContext方法建立一個實例:redux
import React, { createContext } from "react";
const Context = createContext();
複製代碼
Context實例提供兩個組件:Provider和Consumerapi
const ContextProvider = Context.Provider;
const ContextConsumer = Context.Consumer;
複製代碼
其中ContextProvider是數據的發佈方,而ContextConsumer是數據的訂閱方。安全
<ContextProvider value={{ name: "A", age: 18 }}>
<ContextConsumer>
{
context => (
<div>
name:
<span>{context.name}</span>
age:
<span>{context.age}</span>
</div>
)
}
</ContextConsumer>
</ContextProvider>
複製代碼
ContextProvider是數據的發佈方。它擁有一個名爲value的屬性,用於維護數據內容,經過value傳遞給ContextProvider的數據會被髮布出去。 而ContextConsumer是數據的訂閱方,它的props.children是一個函數,接收的參數是被髮布的數據,咱們經過調用這個函數來獲取被ContextProvider發佈的數據,而且返回咱們想要渲染的組件。bash
在這個示例中,咱們最後在頁面上可以顯示出name: A age: 18
。ide
關於調用createContext方法時候的傳參,理論上第一個傳參應該是初始化的數據,可是我使用後發現,若是在ContextProvider的value屬性中不傳入對應的屬性的話,沒法在ContextConsumer中獲取到那個初始化的屬性。函數
const Context = createContext({ name: "A" });
<Context.Provider value={{ age: 18 }}>
<Context.Consumer>
{
context => (
<div>
name:
<span>{context.name}</span>
age:
<span>{context.age}</span>
</div>
)
}
</Context.Consumer>
</Context.Provider>
複製代碼
這個例子中,name的值沒法被獲取到。 我目前還沒找到緣由,若是知道的朋友請告訴我一下,謝謝!ui
關於我這個問題的答案,感謝 @我很西瓜的瓜 朋友的解答,我去試了下,果真是這樣: 只有當Provider不被使用,也就是Context僅僅被用做訂閱數據而不是用來發布數據的時候,調用createContext()方法時傳遞的數據纔會被看成看成源數據發佈;而使用Provider來發布數據的時候,Provider的value屬性會把初始化的數據覆蓋掉。
爲了適應項目的需求,我主要是對ContextProvider和ContextConsumer作了封裝。
因爲在個人項目中組件須要訂閱而且修改和維護被髮布的數據,因此我須要有一個能夠維護這些數據的地方。所以我建立了一個名爲MyProvider的高階組件,並把它放在組件樹的頂層,而各個組件須要訂閱的數據就存放在MyProvider的state中,那麼我只須要維護它的state就能維護這些全局的數據了。
export class MyProvider extends React.Component {
constructor() {
super();
this.state = {
name: "A",
age: 18
};
this.updateContext = this.updateContext.bind(this);
}
updateContext(newData) {
this.setState(Object.assign({}, this.state, newData));
}
render() {
const contextData = { data: this.state };
Object.defineProperty(contextData, "updateContext", {
value: this.updateContext
});
return (
<Context.Provider value={contextData}>
{this.props.children}
</Context.Provider>
);
}
}
複製代碼
MyProvider組件返回的是ContextProvider組件,並把MyProvider的state做爲要發佈的數據綁定到了ContextProvider的value屬性上。 前面講過,因爲其餘組件有要修改被髮布數據的需求,因此我給數據添加了一個不可修改的方法updateContext,這個方法可以接收新的數據並更新MyProvider的state,即更新了被髮布的數據。 最後,MyProvider組件將本身的children原封不動的傳遞給ContextProvider。
考慮到ContextConsumer做爲訂閱方使用比較頻繁,爲了方便其餘組件的使用,我將它封裝到高階組件中,並做爲函數的返回值使用,以下:
export const MyConsumer = Component => {
return props => (
<Context.Consumer>
{context => {
return <Component context={context} {...props} />;
}}
</Context.Consumer>
);
};
複製代碼
MyConsumer函數返回一個高階組件。在這個高階組件中,我把ContextConsumer提供的數據加入到Component的props中,這樣我只須要在export組件的時候調用MyConsumer,而且在組件中使用this.props.context.data
就能獲得被髮布的數據了。以下:
class MyComponent extends React.Component {
addAge() {
const { data: { age }, updateContext } = this.props.context;
const newAge = age + 1;
updateContext({ age: newAge });
}
render() {
const { name, age } = this.props.context.data;
return (
<div>
name:
<span>{name}</span>
age:
<span>{age}</span>
<button onClick={() => this.addAge()}>add age</button>
</div>
);
}
}
export default Consumer(MyComponent);
複製代碼
在這個例子中,點擊按鈕,調用this.props.context.updateContext方法就能夠經過更新MyProvider的state來修改被髮布數據中的age的值。
我折騰了兩天以後才反應過來,這不就是一個相似於redux的東西嗎?
可能因爲我redux用的多了,對於Prvider和Consumer的封裝下意識的作成了相似redux的用法。再加上使用MyProvider的state做爲惟一數據源,又有updateContext這個有點像dispatch的方法來更新數據,乍一看之下,仍是有點redux的影子的。
固然了,我本身寫的徹底沒有redux那麼好用,也沒有reudx那麼嚴謹。因此,後來我又花了一個上午的時間改用了redux。
可是經過此次的實踐,也算是熟悉的new context api的用法,對redux也加深了了解吧。
最後,若是你只是想要訂閱數據,new context api是個不錯的選擇;可是若是你想要修改和維護被髮布的數據,使用redux會更方便和安全。
感謝閱讀,未經容許,請勿轉載:)