react 3 - redux

前言

“如果你不知道是否需要 Redux, 那就是不需要它. “

虽然有这样一句话, 可是作为开发, 却是必须要学会使用的, 只有你会了以后, 才可以来探讨你的项目里面是否需要Redux.

Flux

react为我们的整个应用提供了界面搭建的方法, 如果你的组件够多, 那么就可以像拼积木一样一点点搭建你的应用. 而reactRender是一个纯函数, 只要组件有同样的stateprops, 那么你就会得到同样的view. 而Flux的理念就是使state变成不可写, 而是由action分发的方式在内部处理好数据, 再传给state, 而避免组件自己随意修改state造成不可预知的问题.

Redux

ReduxFlux思想的一种实现方式, 与react结合使用更加方便. 而Redux的设计思想很简单, 就两句话:

  • 应用是一个状态机, 视图与状态是一一对应的.
  • 所有的状态, 保存在一个对象里面.

基本概念

store

在整个应用中, 只有一个store, 它保存着所有数据. 如果想得到某个时间点的数据, 就需要对store生成快照, 生成快照的方法是store.getState(), 那么这个快照自然就叫做state了. 注意, 此state和组件里面的state不是一个概念, 也不会有直接的交集. Redux规定, 一个state对应一个view, 只要state是相同的, 那么生成的view应该也是一样的.

action

state已经有了数据, 那么自然就需要有改变数据的方式, 这个方式就是action, 同时也是唯一的方式. 对于view, 不能直接接触到statestore, 只能通过发送action的方式来改变store. action是一个对象, 其中type属性是必须的, 其他属性都属于携带的信息, 一般通过payload来传递.

1
2
3
4
const action = {
type: 'ADD_TODO',
payload: {},
};

action creator

action既然是一个对象, 那么创建这个对象肯定不会是手写, 而是有一个方法来自动生成, 那么生成这个action的方法即action creator.

1
2
3
4
5
6
static addTodo(v) {
return {
type: ActionType.ADD_TODO,
payload: v,
}
}

addTodo函数即为ADD_TODOaction creator.

dispatch

有了action creator和生成的action, 那么怎么让store知道这个action呢, store.dispatch(action)就是用来发出action的唯一方法.

reducer

声明和使用

当调用store.dispatch之后, 怎么去改变state呢, 这就是reducer的功能了. 每个reducer都是一个函数, 它的参数是当前stateaction, 返回值是一个新的state, 这样view就会根据新的state去绘制界面了.

1
2
3
4
5
6
7
8
9
10
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case ActionType.ADD_TODO:
return state + action.payload;
default:
return state;
}
};
const store = createStore(reducer);

当创建好reducer函数之后, 需要让store知道这个函数, 然后store会在调用dispatch时, 自动触发所有的reducer函数. createStore中的reducer参数就是让store去连接指定的reducer方法.

纯函数

reducer必须是一个纯函数, 意义是每次执行会得到相同的结果. 所以不能调用Data.now()Math.random()等方法. 但是由于是纯函数, 基于函数式编程的理念, 是不能改变原始对象的. 所以如果是对象, 返回值应该是return { ...state, ...newState }. 如果对象结构层级特别深, 那么创建newState就会是一个很麻烦的事情, 而且可能会不小心改变state后也会出现其他错误, 这就是为什么需要immutable的原因.

subscribe

store提供subscribe方法来设置一个监听函数, 只要state发生改变, 那么就会执行这个监听函数. subscribe方法返回一个解除监听的函数unsubscribe, 执行unsubscribe就可以取消监听.

1
2
let unsubscribe = store.subscribe(() => console.log(store.getState()));
unsubscribe();

工具函数

combineReducers

一般都会用到的函数, 如果你的reducer有很多个, 那么你就需要用到这个函数创建出一个rootReducers对象来提供给store连接reducer函数.

1
2
3
4
const RootReducer = combineReducers({
todoReducer: todoReducer,
});
const store = createStore(RootReducer);

connect

connect方法可以为你的展示组件创建对应的容器组件. connect方法签名为connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]).

  • mapStateToProps(state, ownProps) : stateProps
  • mapDispatchToProps(dispatch, ownProps): dispatchProps
  • mergeProps(stateProps, dispatchProps, ownProps): props
  • options为选项, 使用到的地方很少

mapStateToProps(state, ownProps) : stateProps

从名字可以看出, 此函数是把state的属性赋给props(此处state非组件state, 而是storestate). 当state变化, 或者ownProps变化的时候, mapStateToProps都会被调用(此处ownProps是上级组件的props传入到connect生成的高阶组件的props, 不是被connect的组件的ownProps), 计算出一个新的props更新给组件, 并触发组件的componentWillReceiveProps方法.

1
2
3
4
5
const mapStateToProps = (state, ownProps) => {
return {
count: state.count,
}
}

mapDispatchToProps(dispatch, ownProps): dispatchProps

从名字也可以看出来, 是把dispatch方法映射到props上, 不过没有mapStateToProps那么直观. 在ownProps变化时, mapDispatchToProps 也会再次执行, 计算出一个新的值更新到props上.

1
2
3
4
5
const mapDispatchToProps = (dispatch, ownProps) => {
return {
addTodo: (v) => dispatch(action.addTodo(v)),
}
}

传入这个函数以后, 在组件的props上, 就可以使用addTodo方法了.
redux提供了bindActionCreators用来快速将action生成可调用的函数.

1
2
3
4
5
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
addTodo: action.addTodo,
})
}

mergeProps(stateProps, dispatchProps, ownProps): props

这个函数就是用来将上面两个函数生成的值复制到props里面的函数, 如果不传这个函数, 默认调用的是Object.assign方法.

createStore

createStore(reducer, initialState, enhancer): store是创建store的唯一方法, reducer即全部的reducers, initialStatestore的初始化值, enhancer即可以让我们生成加强版本的store的方法.

1
2
3
4
5
6
7
8
9
10
11
//createStore中enhancer有关的源码
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.');
}
return enhancer(createStore)(reducer, preloadedState);
}

可以看到enhancer应该是一个方法, 接收createStore作为参数, 返回一个新createStore的方法, 再传入reducer, preloadedState参数执行.

applyMiddleware

applyMiddleware方法生成的函数即enhancer所表示的值, 从enhancer实现可以看出, applyMiddleware有两种调用方式.

1
2
3
4
5
//1
const createStoreWithMiddleware = applyMiddleware(...middleware)(createStore);
const store = createStoreWithMiddleware(reducer, preloadedState);
//2
const store = createStore(reducer, preloadedState, applyMiddleware(...middleware));

ReduxThunk

重点说说ReduxThunk中间件, 它是与后台交互的必备中间件, 使我们在dispatch中可以发出异步的action. 它的源码也是十分简单.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createThunkMiddleware(extraArgument) {
return function ({ dispatch, getState }) {
return function (next) {
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
};
};
}
var thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

使用的时候也很简单.

1
2
3
4
5
6
7
8
9
10
11
12
13
static addTodo(v) {
return (dipatch, getState) => {
return Service.addTodo(v).then(
(resultValue) => {
let action = {
type: ActionType.ADD_TODO,
payload: resultValue,
};
dipatch(action);
},
);
};
}

Provider

Provider严格来说应该是组件, 不过class本质也是function, 所以放在一起了. Provider组件即让整个应用都可以直接拿到store的组件. 原理是react中的context.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//源码
class Provider extends Component {
getChildContext() {
return {
store: this.props.store
};
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
}
//使用
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#container")
)
作者

Mosby

发布于

2017-08-03

许可协议

评论