最近接到一个简单的内部项目,逻辑并不复杂,就想着 不用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会更方便和安全。
感谢阅读,未经容许,请勿转载:)