橙子

react-redux 总结

在 react 中 引入 redux 可以实现状态管理, 但有些地方有点冗余,所以 react 肯定是会解决这个问题的,所以这次总结 react-redux ,这个库是用来连接 react 和 redux 的

具体使用步骤如下:

1 安装 react-redux

npm install react-redux –save

2 引入 react-redux

index.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';

import TodoList from './components/TodoList';
import Counter from './components/Counter';


render(
<Provider store={store}>
<Counter />
<TodoList />
</Provider>,
window.root
);

注意:

1 Provider:哪个组件需要使用它,就把那个组件放在它里面,它也是一个组件

2 store:是要传进去的store

3 connect

放在 Provider 里的组件要调用这个方法,用来连接store

component/counter.js :

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
36
37
38
39
40
41
42
43
44
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux';
// import { getCountAddAction } from '../store/actions/counter';
import * as actions from '../store/actions/counter';

class Counter extends Component {

render () {
return (
<div>
{ this.props.count }
<button onClick={ this.handleClick }>add</button>
</div>
)
}

handleClick = () => {
this.props.add(6)
}
}

// const mapStateToProps = (state) => ({
// count: state.counter.count
// })

const mapStateToProps = (state) => state.counter;

// 1 返回一个对象,定义这个对象的属性为一个函数,然后在这个函数中调用 dispatch
// const mapDispatchToProps = (dispatch) => ({
// getCountAddAction: (val) => {
// dispatch(getCountAddAction(val))
// }
// })

// 2 redux 提供一个函数bindActionCreators,绑定动作创建者,这个函数的返回值就是mapDispatchToProps所需要的返回值
// const mapDispatchToProps = (dispatch) => bindActionCreators(actions, dispatch)

// 3 由于每次都是相同的步骤,所以 react 允许我们直接传 action 进去
// actions

// 由于每次mapDispatchToProps都是 传 dispatch 进去,然后返回

export default connect(mapStateToProps, actions)(Counter);

store/reducers/counter.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import * as Types from '../actionTypes';

const initState = {
count: 0
}


export default (state=initState, action) => {
const newState = JSON.parse( JSON.stringify(state));

switch (action.type) {
case Types.COUNT_ADD:
newState.count = newState.count + action.n;
return newState;
}

return state;
}

store/actions/counter.js :

1
2
3
4
5
6
7
8
import * as Types from '../actionTypes';

export const add = (n) => {
return {
type: Types.COUNT_ADD,
n
}
}

注意:

1 connect:接收两个参数:mapStateToProps,mapDispatchToProps

2 mapStateToProps:把要订阅store中的哪个值传进去

3 mapDispatchToProps:调用的是哪个action 传进去

4 这样在点击事件 handleClick 中就可以通过 this.props调用 store 中的方法,this.props.add(5)

到这里,react-redux 的使用步骤大体是这样的,这个过程发现 代码都是有问题解决问题,再进一步优化再优化!

4 redux-thunk、redux-promise、redux-logger、redux-saga 中间介

当我们在组件中需要获取一些初始数据,这些数据并不是自己私有的,是需要传给store的,而这些数据最初是通过axios等异步方式获取时,我们可以把这个axios方法写在 componentDidMount 方法中,但这样这个过程是:组件在 componentDidMount 获取数据,通过 dispatch 传给 store

既然是这样,那干嘛不直接在 store 中去获取

store/actions/todoList.js :

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
import * as Types from '../actionTypes';
import axios from 'axios';

export const getInitList = list => {

// 1 axios请求是异步的,显然不可以
// let data;
// axios.get('list.json').then(res => {
// data = res;
// })

// 2 是 return 了,但并没有返回出来
// return () => ({
// axios.get('list.json').then(res => {
// dispatch({
// type: Types.GET_INIT_LIST,
// list: res
// })
// })

// return {
// type: Types.GET_INIT_LIST,
// list: data
// }
}

嗯,react 是一个成熟的框架,它显然可以通过别的方式在 store 中获取,配和redux 中的 redux-thunk、redux-promise 可以实现

redux-thunk

安装:npm install redux-thunk –save

store/index.js :

1
2
3
4
5
6
7
8
9
10
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import thunk from 'redux-thunk';

const store = createStore(
reducer,
applyMiddleware(thunk)
);

export default store;

store/actions/todoList.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as Types from '../actionTypes';
import axios from 'axios';

export const getInitList = list => {

return dispatch => {
axios.get('list.json').then(res => {
dispatch({
type: Types.GET_INIT_LIST,
list: res
})
})
}
}
redux-promise

安装:npm install redux-promise –save

store/index.js :

1
2
3
4
5
6
7
8
9
10
11
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import thunk from 'redux-thunk';
import promise from 'redux-promise';

const store = createStore(
reducer,
applyMiddleware(thunk, promise)
);

export default store;

store/actions/todoList.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as Types from '../actionTypes';
import axios from 'axios';

export const getInitList = list => {

return new Promise( (resolve, reject) => {
axios.get('list.json').then(res => {
resolve({
type: Types.GET_INIT_LIST,
list: res
})
})
})
}
redux-logger

这个中间介 是为了知道在任务派发之前和派发之后的一个状态

安装:npm install redux-logger –save

store/index.js :

1
2
3
4
5
6
7
8
9
10
11
12
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducers';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import logger from 'redux-logger';

const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger)
);

export default store;

这时候就可以在控制台看到任务派发前和派发后的状态,如下:

在前面的 index.js 中把 window那一句删除了,但它是用来在控制台打开 redux 的,现在把它加回来,这个在github上有使用说明

index.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducers';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import logger from 'redux-logger';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
reducer,
composeEnhancers( applyMiddleware(thunk, promise, logger) )
);

export default store;
redux-saga

这个中间介作用和 redux-thunk 一样,也是在 action 中可以调用一次网络请求,但使用的方式有很大不同

安装:npm install redux-saga –save

store/index.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import createSagaMiddleware from 'redux-saga';
import mySaga from './sagas';

const sagaMiddleware = createSagaMiddleware();

const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
);

sagaMiddleware.run(mySaga);

export default store;

store/sagas.js , 在这个文件中集中处理对异步的请求,它能够感知到每一次任务派发的请求,当派发某个任务时,这里就执行相应的方法

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
import { takeEvery, put } from 'redux-saga/effects';
import axios from 'axios';
import * as Types from './actionTypes';
import * as actions from './actions/todoList';

function* mySaga () {
yield takeEvery(Types.ADD_TODO_ITEM, addItem);
yield takeEvery(Types.GET_TODO_DATA, getTodoData);
}

function* addItem () {
console.log('add');
}

function* getTodoData () {
try {
const data = yield axios.get('list.json');
const action = actions.getInitList(data);
yield put(action);
} catch (err) {
console.log(err);
}
}
// put: 这里不需要用 dispatch 进行任务的派发,它提供了一个 put 方法,可以用它来进行任务的派发
export default mySaga;

component/todoList.js :

1
2
3
componentDidMount () {
this.props.getTodoData();
}

这四个中间介是目前我了解到的,基本区别如下:

redux-thunk:帮助action接受函数,能在redux发送一次异步请求

redux-promise:帮助action接收promise

redux-logger:了解派发之前和派发之后的状态

redux-saga:和redux-thunk有点相似,也能在redux中发送一次异步请求,但两者使用方式有很大不同