项目作者: SUNNERCMS

项目描述 :
redux、redux-thunk、redux-saga、react-redux以及mobx、dva状态管理的使用
高级语言: JavaScript
项目地址: git://github.com/SUNNERCMS/Redux-mobx-dva_learning.git
创建时间: 2020-06-29T15:46:26Z
项目社区:https://github.com/SUNNERCMS/Redux-mobx-dva_learning

开源协议:

下载


tag-v1: 基本的redux使用流程

1、store文件,用来生成store来作为数据仓库

(1)引入createStore方法,用来生成 Store创建数据存储仓库
(2)createStore函数接受另一个函数作为参数,返回新生成的 Store 对象

  • index.js
    ```js
    import { createStore } from ‘redux’;
    import reducer from ‘./reducer.js’;

const store = createStore(reducer);

export default store; //暴露出去

  1. - reducer.js
  2. ```js
  3. //默认数据,整个项目中需要管理的数据信息
  4. const defaultState = {
  5. inputValue : 'Write Something!',
  6. list:[
  7. '早上4点起床,锻炼身体',
  8. '中午下班游泳一小时'
  9. ]
  10. }
  11. export default (state = defaultState, action) => {
  12. return state
  13. }

2、建立的组件,在组件中使用store中的数据

使用store仓库中的数据,需要先引入,再建立快照
如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到

  1. import React, { Component } from 'react';
  2. import 'antd/dist/antd.css'
  3. import { Input, Button, List } from 'antd'
  4. import store from './store';
  5. //将这里数据放到了仓库中管理
  6. // const data=[
  7. // '早8点开晨会,分配今天的开发工作',
  8. // '早9点和项目经理作开发需求讨论会',
  9. // '晚5:30对今日代码进行review'
  10. // ]
  11. class TodoList extends Component {
  12. // constructor(props) {
  13. // super(props);
  14. // this.state = store.getState();
  15. // }
  16. render() {
  17. const {
  18. inputValue,
  19. list
  20. } = store.getState();
  21. return (
  22. <div style={{margin:'10px'}}>
  23. <div>
  24. <Input placeholder={inputValue} style={{ width:'250px', marginRight:'10px'}}/>
  25. <Button type="primary">增加</Button>
  26. </div>
  27. <div style={{margin:'10px',width:'300px'}}>
  28. <List
  29. bordered
  30. dataSource={list}
  31. renderItem={item=>(<List.Item>{item}</List.Item>)}
  32. />
  33. </div>
  34. </div>
  35. );
  36. }
  37. }
  38. export default TodoList;

tag-v2: redux的基本完整流程-todolist示例

在v1版本上增加了,通过dispatch分发action给数据仓库store,改变数据的处理,详细代码及注释见v2。
相关代码如下:

  1. <Input
  2. placeholder={inputValue}
  3. style={{ width:'250px', marginRight:'10px'}}
  4. onChange={this.intputOnchange}
  5. />
  1. // 输入框内容改变事件处理函数,改变之后发生动作,来改变store仓库中数据
  2. //改变store数据的唯一途径:通过dispatch进行action驱动
  3. //action为动作说明对象:动作类型+动作数据
  4. intputOnchange = (e) => {
  5. const action = {
  6. type: 'change_input_value',
  7. value: e.target.value
  8. };
  9. store.dispatch(action);
  10. }
  11. //reducer.js文件
  12. if(action.type === "change_input_value") {
  13. //创建仓库数据副本,(记住:Reducer里只能接收state,不能改变state。)
  14. let newState = JSON.parse(JSON.stringify(state));
  15. newState.inputValue = action.value;
  16. return newState;
  17. }
  18. // Todolist.js
  19. //订阅Redux的状态,当状态改变时,执行相应函数
  20. store.subscribe(this.storeChange);
  21. //store数据仓库改变时,获取仓库数据,对该组件进行setState触发render渲染
  22. storeChange = () => {
  23. this.setState(store.getState());
  24. }

tag-v3: redux结构分层(action类型管理文件+action动作管理文件)

  • actionType.js文件用来统一管理action的类型,因为之前在reducer和需要dispatch action的地方均需要指明action的类型,并且二者保持一致,不便于管理维护。
  • actionCreators.js文件,用来创建action对象,可以是函数形式返回一个包含类型和值的对象,用来生成需要的action对象,供dispatch传递给store仓库,来进行数据更新操作。

相关代码如下:

  1. //统一用来管理action类型
  2. export const CHANGE_INPUT_VALUE= 'change_input_value';
  3. export const ADD_INPUT_VALUE= 'add_input_value';
  4. export const DELETE_ITEM= 'delete_item'
  1. import {
  2. CHANGE_INPUT_VALUE,
  3. ADD_INPUT_VALUE,
  4. DELETE_ITEM
  5. } from './actionType.js';
  6. // 输入框内容改变处理动作
  7. export const changeInputValueAction = (value) => ({
  8. type: CHANGE_INPUT_VALUE,
  9. value
  10. });
  11. // 增加按钮点击处理动作
  12. export const addInputValueAction = () => ({
  13. type: ADD_INPUT_VALUE
  14. });
  15. // 删除列表某一项处理动作
  16. export const deleteItemAction = (index) => ({
  17. type: DELETE_ITEM,
  18. index
  19. });
  20. intputOnchange = (e) => {
  21. const action = changeInputValueAction(e.target.value);
  22. store.dispatch(action);
  23. }

tag-v4: Todolist函数式组件拆分+axios请求与redux结合

  • TodoList.js:处理业务逻辑,包括交互事件处理,数据请求,分发action等
  • TodoListUI.js:函数式组件用来负责渲染界面展示

axios请求与redux结合过程:
1、声明axios获取数据更新到store的action类型

  1. export const GET_LIST= 'get_list'

2、创建action对象,用于dispatc分发给store

  1. // 将请求获取的列表数据更新到数据仓库
  2. export const getListAction = (data) => ({
  3. type: GET_LIST,
  4. data
  5. });

3、进行请求并执行分发dispatch,数据mock可以用easy-mock或者用友的yapi

  1. //获取列表数据
  2. requestListData = () => {
  3. axios.get("https://mock.yonyoucloud.com/mock/10365/reactdemo/todolist")
  4. .then(res => {
  5. const action = getListAction(res.data.data.list);
  6. store.dispatch(action);
  7. });
  8. }

tag-v5: redux-thunk与reudx的结合使用

前言:在控制台调试这些仓库里的数据,需要使用Redux DevTools谷歌插件,在创建仓库时,进行判断是否有该插件,有的话就使用。

  1. const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

问题:如果按照官方文档来写,直接把thunk放到createStore里的第二个参数就可以了,但以前我们配置了Redux Dev Tools,已经占用了第二个参数。

官方文档给的方法:

  1. const store = createStore(
  2. reducer,
  3. applyMiddleware(thunk)
  4. ) // 创建数据存储仓库

这样写是完全没有问题的,但是我们的Redux Dev Tools插件就不能使用了,如果想两个同时使用,需要使用增强函数。使用增加函数前需要先引入compose。

  1. import { createStore , applyMiddleware ,compose } from 'redux' // 引入createStore方法
  2. import reducer from './reducer'
  3. import thunk from 'redux-thunk'
  4. const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
  5. window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
  6. const enhancer = composeEnhancers(applyMiddleware(thunk))
  7. const store = createStore( reducer, enhancer) // 创建数据存储仓库
  8. export default store //暴露出去

(1)利用compose创造一个增强函数,就相当于建立了一个链式函数.(2)有了增强函数后,就可以把thunk加入进来了,这样两个函数就都会执行了。(3)这时候直接在createStore函数中的第二个参数,使用这个enhancer变量就可以了,相当于两个函数都执行了。也许你对增加函数还不能完全理解,其实你完全把这个想成固定代码,直接使用就好.

redux-thunk与reudx的结合使用过程:
1、安装reudx-thunk npm install --save redux-thunk
2、引用redux的applyMiddleware,来在redux中使用中间件 applyMiddleware(thunk),并在创建函数时作为参数项注册,见上文前言。
3、在Dispatch一个Action之后,到达reducer之前,可以使用中间件来进行日志记录、创建崩溃报告,调用异步接口等。
4、将在组件执行的接口请求逻辑,转移到actionCreator.js文件中。

以前actionCreators.js都是定义好的action,根本没办法写业务逻辑,
有了Redux-thunk之后,可以把TodoList.js中的componentDidMount业务逻辑放到这里来编写。
也就是把向后台请求数据的代码放到actionCreators.js文件里。
(以前的action是对象,现在的action可以是函数了,这就是redux-thunk带来的好处)

  1. //actionCreator.js
  2. //获取列表数据(需要包含更新动作getListAction)
  3. //这个函数可以直接传递dispatch进去,这是自动的,然后我们直接用dispatch(action)传递就好了
  4. export const getTodoList = () => {
  5. return (dispatch) => {
  6. axios.get("https://mock.yonyoucloud.com/mock/10365/reactdemo/todolist")
  7. .then(res => {
  8. const action = getListAction(res.data.data.list);
  9. dispatch(action);
  10. });
  11. }
  12. }
  1. //TodoList.js
  2. //获取列表数据
  3. // requestListData = () => {
  4. // axios.get("https://mock.yonyoucloud.com/mock/10365/reactdemo/todolist")
  5. // .then(res => {
  6. // const action = getListAction(res.data.data.list);
  7. // store.dispatch(action);
  8. // });
  9. // }
  10. componentDidMount() {
  11. // this.requestListData();
  12. const action = getTodoList();
  13. //使用redux-thunk,在dispathc分发action到reducer之前,进行了axios请求
  14. store.dispatch(action);
  15. }

tag-v6: redux-saga与reudx的结合使用

安装reudx-saga npm install --save redux-saga

1、在store中引入saga,并注册到创建的store仓库中,运行saga,运行的内容就是创建的saga文件,用来管理业务代码的文件

  1. import { createStore, applyMiddleware, compose } from 'redux'; // 引入createStore方法,用来生成 Store
  2. import reducer from './reducer.js';
  3. import mySagas from './sagas.js';
  4. //引入redux的saga中间件,并创建
  5. import createSageMiddleware from 'redux-saga';
  6. const sagaMiddleware = createSageMiddleware();
  7. //创建增强函数,使得Redux Dev Tools插件和redux-thunk中间件都能使用
  8. const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
  9. window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
  10. const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
  11. // 创建数据存储仓库,createStore函数接受另一个函数作为参数,返回新生成的 Store 对象
  12. const store = createStore(reducer, enhancer);
  13. //使用saga中间件来运行引入的mySagas,监听mySagas文件
  14. sagaMiddleware.run(mySagas);
  15. export default store; //暴露出去

2、actionCreators.js文件中,增加用来获取列表动作的action,根据这个action类型到sagas.js文件中去查找真正用来处理业务请求的逻辑

  1. // saga-用来获取列表数据的动作
  2. export const getMyListAction = () => ({
  3. type: GET_MY_LIST,
  4. });

3、sagas.js文件,mySaga函数是入口文件,在store/index中由saga中间件运行 sagaMiddleware.run(mySagas);,用来捕获action,并根据类型调用相对应的处理函数

  1. //管理业务逻辑
  2. //mySaga来作为入口函数。在入口函数中捕获传递过来的action类型,根据类型不同调用不同的方法。
  3. import { takeEvery, put } from 'redux-saga/effects';
  4. import { GET_MY_LIST } from './actionType';
  5. import { getListAction } from './actionCreators.js';
  6. import axios from 'axios'
  7. function* mySaga() {
  8. //等待捕获action
  9. yield takeEvery(GET_MY_LIST, getList);
  10. }
  11. function* getList() {
  12. const res = yield axios.get("https://mock.yonyoucloud.com/mock/10365/reactdemo/todolist");
  13. //调用aciont中的getListAction函数生成action,将请求获取的列表数据更新到数据仓库
  14. const action = getListAction(res.data.data.list);
  15. yield put(action);
  16. }
  17. export default mySaga

4、TodoList.js组件中调用actionCreators.js中的getMyListAction,用来发起获取列表数据的动作

  1. componentDidMount() {
  2. const action = getMyListAction();
  3. store.dispatch(action);
  4. }

tag-v5:redux-thunk和tag-v6:redux-saga的使用感受:
区别主要在于对副作用的管理上
(1)redux-thunk使得原本只能是对象的action可以是函数,用来处理一些异步请求的操作,actonCreators.js中既有对象的action,也有函数的action。
(2)redux-saga可以使用saga中间件运行创建的saga.js文件,将actionCreators.js中函数形式的action(处理异步请求)放到来saga.js文件中,这样看来actionCreators.js中就全部是对象的action,saga.js中根据action的类型专门用来处理业务逻辑,再结合saga使用来ES6的Generator功能,让异步的流程更易于读取,写入和测试,结构也更加清晰
(3)在不引入中间件时,请求的逻辑处理直接放在了组件中,在请求函数中调用了数据更新action的动作。

tag-v7: react-redux+redux-saga与reudx的结合使用

无论是redux-thunk还是redux-saga和redux结合使用,在组件中要想使用store中的数据,都需要引入这个store,并进行数据仓库的监听。
React-Redux这是一个React生态中常用组件,它可以简化Redux流程,通过和以上中间件的使用对比,react-redux组件带来的变化主要是改变了组件使用store的方式、获取store中state的方式、获取action的方式,这里不和redux-thunk以及redux-saga的使用冲突,相对而言,更是一种补充。

相关代码如下:

  1. import store from './store';
  2. constructor(props) {
  3. super(props);
  4. //store.getState();获取到数据仓库中的数据对象
  5. this.state = store.getState();
  6. //订阅Redux的状态,当状态改变时,执行相应函数
  7. store.subscribe(this.storeChange);
  8. }
  9. intputOnchange = (e) => {
  10. const action = changeInputValueAction(e.target.value);
  11. store.dispatch(action);
  12. }
  13. //store数据仓库改变时,获取仓库数据,对该组件进行setState触发render渲染
  14. storeChange = () => {
  15. this.setState(store.getState());
  16. }

react-redux的使用过程:
1、index文件中,引入Provider提供器,以及store,将store提供给Provider所包括的组件使用,不在组件中进行显示引用。

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import TodoList from './TodoList';
  4. import { Provider } from 'react-redux';
  5. import store from './store/index';
  6. //声明一个App组件,然后这个组件用Provider进行包裹。
  7. // <Provider>是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了,这也是React-redux的核心组件了
  8. const App = (
  9. <Provider store = {store}>
  10. <TodoList></TodoList>
  11. </Provider>
  12. )
  13. ReactDOM.render(App, document.getElementById('root'));

2、组件TodoList中使用connect连接器,连接的内容为:数据(将store中数据state映射到该组件props中)+ 事件(将actionCretors中action动作对象,映射给该组件props中方法,供该组件进行调用进行事件处理)

  1. componentDidMount() {
  2. this.props.getMyList();
  3. }
  4. //映射关系就是把原来的state,(也即是原本通过store.getState()来获取仓库中数据)映射成组件中的props属性
  5. const stateToProps = (state) => {
  6. return {
  7. inputValue: state.inputValue,
  8. list: state.list
  9. }
  10. }
  11. const dispatchToProps = (dispatch) => {
  12. return {
  13. // 输入框内容改变事件处理函数,
  14. intputOnchange(e){
  15. const action = changeInputValueAction(e.target.value);
  16. dispatch(action);
  17. },
  18. //增加按钮点击事件,触发动作之后根据动作类型在reducer中将input改变的最新值,添加到list中,来驱动列表更新
  19. addInputValue(){
  20. const action = addInputValueAction();
  21. dispatch(action)
  22. },
  23. //删除列表某一项
  24. deleteItem(index){
  25. const action = deleteItemAction(index);
  26. dispatch(action)
  27. },
  28. //获取列表数据
  29. getMyList() {
  30. const action = getMyListAction();
  31. dispatch(action);
  32. }
  33. }
  34. }
  35. export default connect(stateToProps,dispatchToProps)(TodoList);