Redux 是什么
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 目前一般与 React 配合使用。React 提供了 React-redux 库,两者能轻松结合起来。
开始之前需要知道的东西
为什么需要状态管理?
- 多次向下传递 props 过于繁琐
- 同一个 api 可能在不同情况下请求多次
如果你有 Vuex 的开发经验,那么上手起来会很快。
简单上手
在 Redux 中,状态 (state) 是通过 action 改变的,而 action 其实调用了 reducer 方法。一个 action 能告诉 reducer,下一步做出的动作 (type) 是什么,应该如何去改变数据。
对比 Vuex,一般在 Vuex 中我们通过 action 提交 (commit) 一个 state 的更改。而在 Redux 中是 action 调用了 reducer。
首先我们新建一个 store。
js
代码语言:javascript复制1// src/store/index.js
2import { createStore } from 'redux';
3
4import reducer from './reducers';
5export default createStore(reducer);
COPY
新建 store 需要传入一个 reducer。在这里我们来写一个小范例,state 记录一个数据,通过 action 改变他的大小。
js
代码语言:javascript复制1import T from '../actions/types';
2// import { add } from '../actions';
3import { combineReducers } from 'redux';
4
5const initState = {
6 num: 1
7};
8
9const change = (state = initState, action) => {
10 const { type, num } = action;
11 console.log(num);
12
13 switch (type) {
14 case T.ADD:
15 return {
16 num: state.num
17 };
18 case T.REDUCE:
19 return {
20 num: --state.num
21 };
22 default:
23 return state;
24 }
25};
26
27export default combineReducers({
28 change
29});
COPY
前面提到 action 调用 reducer,所以我们需要一个 action。
js
代码语言:javascript复制1import T from './types';
2
3export function add(num) {
4 return {
5 type: T.ADD,
6 num
7 };
8}
9export function reduce(num) {
10 return {
11 type: T.REDUCE,
12 num
13 };
14}
COPY
js
代码语言:javascript复制1// store/actions/types.js
2export const types = Object.freeze({
3 ADD: 'ADD',
4 REDUCE: 'REDUCE'
5});
6
7export default types;
COPY
一个 action 由一个 type 和一个 payload 组成,type 是告诉 reducer 应采取哪种更新方式。
我们再来看一下 reducer 由什么组成。
js
代码语言:javascript复制1// src/store/reducers/index.js
2import T from '../actions/types'; // 一般可以把 actionTypes 统一记录到一个文件中
3import { combineReducers } from 'redux';
4
5const initState = { // 原始 states
6 num: 1
7};
8
9// ADD 变量名作为 getStates 中的 key
10const change = (state = initState, action) => {
11 // state 默认不存在所以需要制定默认值,也就是初始化,初始化之后每次调用都会传入未被更新的 state
12 // action 中记录了我们制定的 type, payload, 这里是 num
13 const { type, num } = action;
14
15 switch (type) {
16 // 判断类型,改变数据。应返回一个新的 state。注意:必须是新对象而不是一个引用
17
18 case T.ADD:
19 return {
20 num: state.num num
21 };
22 case T.REDUCE:
23 return {
24 num: --state.num
25 };
26 default:
27 // 不匹配的 action 类型,直接返回。
28 // 一个 action 会被多个 reducer 接收,注意类型的监听。
29 return state;
30 }
31};
32
33// 使用 combineReducers 连接多个 reducers,虽然这里就一个
34export default combineReducers({
35 change
36});
COPY
至此一个存储数字,并能通过 action 改变他的大小的 store 就写好了。然后我们在 react 中使用它。
js
代码语言:javascript复制1// app.js
2
3import store from './store';
4ReactDOM.render(
5 <App {...store} />,
6 document.getElementById('root')
7);
COPY
将 store 实例挂载到根组件,下一层组件能通过 props 接收。
通过 getState 方法我们可以拿到 store 中存储的值,比如我想拿到 change
reducer 中的 state。
console.log(this.props.getState().change);
也可以通过 dispatch 方法修改 state 的值。this.props.dispatch(add(1))
到这里 redux 基本算是入门了,接下来是和 React 绑定。不知道有没有注意到开始从根组件传参只能传一层,违背了 store 随时随地使用的原理。这时候 react-redux 登场了。
react-redux 提供一个 Provider 高阶组件,传入一个 store,接下来在下层的所有子组件中用只要使用 connect 方法就可获取到 store。
js
代码语言:javascript复制1import store from './store';
2import { Provider } from 'react-redux';
3ReactDOM.render(
4 <Provider store={store}>
5 <App />
6 </Provider>,
7 document.getElementById('root')
8);
COPY
在子组件中不再导出一个默认的 Component,而是导出一个 connect Component。
js
代码语言:javascript复制1import React, { Component } from 'react';
2import { connect } from 'react-redux';
3import { add, reduce } from '../store/actions';
4import styles from './style.module.css';
5class Demo extends Component {
6 render() {
7 console.log(this.props);
8
9 return (
10 <div className={styles['wrap']}>
11 <button onClick={() => this.props.add(1)}> </button>
12 <span style={{ color: '#fff' }}>{this.props.num}</span>
13 <button onClick={() => this.props.reduce(1)}>-</button>
14 </div>
15 );
16 }
17}
18const mapStateToProps = state => {
19 return {
20 num: state.change.num
21 };
22};
23const mapDispatchToProps = dispatch => {
24 return {
25 add: num => dispatch(add(num)),
26 reduce: num => dispatch(reduce(num))
27 };
28};
29
30export default connect(mapStateToProps, mapDispatchToProps)(Demo);
COPY
mapStateToProps
仔细看是不是和 mapGetter 有点像。mapStateToProps
接收一个 state,返回一个对象,这个对象会直接被挂载到 props 上。
mapDispatchToProps
又如同 mapActions,他返回一个对象,这个对象也会直接被挂载到 props 上。这个对象中的函数返回一个函数,可以接收参数,并调用 dispatch 改变 state。