React和它的小伙伴们(2) - Redux与状态管理
Redux
根据前一篇文章中最开始说的,React就是一个函数对应关系,那么相应的,如果作为一个大应用来说就可能有些状态是需要跨组件共用的,那么自然而然地就会想到用一个仓库来把它保存起来,这里就产生了Redux这个“思维模式”。
Redux其实是一个可以脱离React使用的思维模式,这种方式具有四个想法:
- 提高修改数据(应用状态)的门槛
主要就是引入action和dispatch两个方式来对全局的状态进行修改,而不是仅仅在每个组件内部进行修改和处理。 - 抽象出一个初始化的createStore方法,内部包含一个store,dispatch和getState三种方法。
- 加入订阅者模式来改变视图。(也就是subscribe方法)
- 引入Reducer函数(纯函数)
有了这四个想法,那么就直接来代码:)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//初始化的createStore创建全局状态集方法
function createStore(reducer) {
//store中的state
let state = null;
//存储所有的监听事件
const listners = [];
//添加订阅者
const subscribe = (listner) => listners.push(listner);
//暴露给外部获取状态
const getState = () => state;
//暴露给外部根据初始化传入的reducer方案进行修改状态
const dispatch = (action) => {
state = reducer(state, action);
listner.forEach((listner) => listner());
}
//初始化操作(reducer中switch的default事件)
dispatch({});
//暴露给外部的所有方案
return {getState, subscribe, dispatch};
}
//使用方案
function reducer(state, action) {
/*初始化state以及定义本store所需要的action*/
}
//调用初始化方案进行创建Store
let store = createStore(reducer);
//将重新渲染视图这个时间加入订阅者中
store.subscribe(() => renderApp(store.getState()));
//根据初始化的state,初始化App
renderApp(store.getState());
//需要更改状态时,使用这个
store.dispatch(action);
React-Redux
下面要做的就是将Redux与React连接,那么如何将一个全局的State分发到所有的组件中呢?
初步考虑:把这个组件状态放入全局可以获得的Context中,并且向下传递。 那么这样的方案会带来两个问题:
- 有大量的重复逻辑 -> 每个组件都要取出context, 从中取出需要的store中的state
- 对context的依赖性过强 -> 如果这个组件需要跨应用复用就难以为继了。
真正的框架中,使用了高阶组件的方式来解决第一个问题。 那么第二个问题就是尽量使用dumb组件(类似于纯函数),这样仅仅依赖于props来渲染组件,提高组件的可复用性。
那么具体是如何实现的呢? Code please:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//connect函数首先接收两个map对应的方法,并且返回一个接收需要被包装组件作为参数的包装方法,并且return出去
//比如C组件,需要包装的话,调用方式为connect(mSTP, mDTP)(C);
export connnect = (mapStateToProps, mapDispatchToProps) => (Wrapped) => {
export Connect extends Component {
static contextTypes = {
store: PropTypes.Object,
}
constructor() {
super();
this.store = {allProps: {}};
}
_updateProps() {
const {state} = this.context;
let stateProps = mapStateToProps ? mapStateToProps(store.getState) : this.props;
let dispatchProps = mapDispatchToProps(store.dispatch);
this.setState({allProps:{...stateProps,...dispatchProps, ...this.props}});
}
render() {
return <Wrapped {...this.state.allProps}/>
}
}
return Connect;
}
那么接下来来看重要附加内容,两个参数:
- mapStateToProps: 或得到的Props如何对应到数据结构中(本组件的状态存储)
- mapDispatchToProps: 如何修改state的dispatch方法(自定义)
那么最后一个问题就是怎么样分发这里的状态呢? 整合Context, 在最顶层给出一个Provider组件,它就是一个容器组件, 会把嵌套的内容原封不动作为自己的子组件渲染出来. 它还会把外界传给它的props.store 放到 context, 这样子组件 connect 的时候都可以获取到.
Let’s see it
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Provider extends Component {
static propTypes = {
store: PropTypes.object,
children: PropTypes.any
}
static childContextTypes = {
store: PropTypes.object
}
getChildContext () {
return {
store: this.props.store
}
}
render () {
return (
<div>{this.props.children}</div>
)
}
}
/*
在需要使用Redux的地方套用这个Provider组价最为根节点,这样connect函数获
取到的props内容就都是这里拿到的store
*/
以上就是React中对于状态管理的Redux方案。当然这里也就只描述了ClassCompnent情况下的内容,如果使用Hooks的话,对于状态管理其实也大同小异。这个话题可以留待下次讨论~。
修改的方式有了,那么如何获得数据对其进行修改?如果在变化视图之前需要进行一些其他的操作呢?那就需要下一次讲到的中间件的登场了。 本系列未完待续。
This post is licensed under CC BY 4.0 by the author.