1、redux-saga
① redux-saga异步解决方案
-
上面使用redux-thunk可以看到,异步的请求操作全被放在了action里面去执行,这样让其功能不够纯粹,所以需要用到redux-saga,它可以将异步操作单独提取出去,让action只需要声明方法即可,这样更易于维护,逻辑也更精确。首先安装:
npm install redux-saga
。 -
这个案例我也是用到了上面的服务端的代码,用来进行异步操作,代码和上面案例的一样,只需要开启服务器即可。
-
来看一下reducer的代码:
-
来看一下action方法:
// src/Store/Actions/Person.action.js // 定义一个 action export const load_person = () => ({ type: 'load_person' })
-
下面就是saga相关的代码,在Store里面创建一个Saga文件夹,再在这个文件夹中创建一个
Person.saga.js
文件,用来编写对应的saga代码:// src/Store/Saga/Person.saga.js // 创建的 person 状态管理的 saga 中间件文件 // 导入 axios import axios from 'axios' // 导入 redux-saga/effects 中的两个方法 import { takeEvery, put } from 'redux-saga/effects' // 创建 loadPerson,进行异步操作 function* loadPerson() { // 发送请求,获取用户数据 const persons = yield axios.get('http://localhost:3005/api/getUsers') .then(response => response.data) // 请求成功后,调用 load_person_success,并将数据传递过去 yield put({ type: 'load_person_success', payload: persons }) } // 导出 PersonSaga 方法 export default function* PersonSaga() { // 拦截在 action 中定义的 load_person 方法 yield takeEvery('load_person', loadPerson) }
注意:
redux-saga/effects
中的takeEvery()
方法,是用来进行拦截action事件的,其有两个参数:- 参数一:action的事件名
- 参数二:自己创建的异步操作函数
redux-saga/effects
中的put()
方法,只用了进行调用action方法进行传递数据的,其有两个参数:- 参数一:action的事件名
- 参数二:要传递的数据
-
来查看reducer里面的代码:
// src/Store/Reducer/Person.reducer.js // 用户信息状态管理文件 // 初始化一个默认的 state const defaultState = { person: [] } // 创建 PersonReducer const PersonReducer = (state = defaultState, action) => { // 根据传递的方法名不同,进行不同的方法操作 switch (action.type) { case 'load_person_success': return { person: action.payload } default: return state } } // 导出 export default PersonReducer
-
合并reducer:
// src/Store/Reducer/index.js // 合并 reducer // 导入 combineReducers 方法 import { combineReducers } from "redux"; // 导入 reducer import PersonReducer from './Person.reducer' // 导出合并的 reducer export default combineReducers({ PersonReducer })
-
-
在主入口文件
index.js
中,导入redux-saga中的createSagaMiddleware()
方法,使用saga创建中间件:// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; // 导入 redux import { createStore, applyMiddleware } from 'redux' // 导入 react-redux 的 Provider 模块 import { Provider } from 'react-redux' // 导入 redux-saga 中的 createSagaMiddleware 方法 import createSagaMiddleware from 'redux-saga' // 导入 ContentReducer import totalReducer from './Store/Reducer' // 导入创建的 person saga import PersonSaga from './Store/Saga/Person.saga' // 创建一个 saga 中间件 const sagaMiddleware = createSagaMiddleware() // 初始化 store const store = createStore(totalReducer, applyMiddleware(sagaMiddleware)) // 注册自己定义的 saga 方法,必须要在初始化 store 之后 sagaMiddleware.run(PersonSaga) ReactDOM.render( <React.StrictMode> {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */} <Provider store={ store }> <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
-
来看看主入口页面
App.jsx
的代码:// src/App.jsx // redux 工作流程梳理 import { Component } from 'react'; // 导入 connect 函数 import { connect } from "react-redux"; // 导入 bindActionCreators import { bindActionCreators } from "redux"; // 导入 Person reducer 里面的方法 import * as PersonActions from "./Store/Actions/Person.action"; class App extends Component { handleClick = () =>{ this.props.load_person() } render() { console.log(this.props); return ( <div> <button onClick={ this.handleClick }>获取数据</button> </div> ) } } // 获取 store 中的数据 const mapStateToProps = (state) => { return { person: state.PersonReducer.person } } // 自动触发 action 的函数 const mapActionsToProps = (dispatch) => { return { ...bindActionCreators(PersonActions, dispatch), } } export default connect(mapStateToProps, mapActionsToProps)(App);
-
来看看页面效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIQB6ZHv-1654949415634)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/abe2f37244e8471bae9494cc4476d0e4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image)]
② redux-saga拆分与合并
-
saga的拆分与合并,与reducer的拆分与合并是一样的逻辑,防止以后saga越来越多,所以将其单独导入一个文件中,再整体导出。
-
在
Store/Saga
文件夹中创建一个root.saga.js
文件,用来合并saga:// src/Store/Saga/root.saga.js // 合并 saga // 导入 redux-saga/effects 中的 all 方法,用来合并 saga import { all } from 'redux-saga/effects' // 导入 PersonSaga import PersonSaga from './Person.saga' // 导出 export default function* RootSaga() { // 合并 saga yield all([ // 因为 saga 是一个函数,所以需要调用 PersonSaga() ]) }
-
在主入口文件
index.js
中进行导入合并的saga:// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; // 导入 redux import { createStore, applyMiddleware } from 'redux' // 导入 react-redux 的 Provider 模块 import { Provider } from 'react-redux' // 导入 redux-saga 中的 createSagaMiddleware 方法 import createSagaMiddleware from 'redux-saga' // 导入 ContentReducer import totalReducer from './Store/Reducer' // 导入合并 saga import RootSaga from './Store/Saga/root.saga' // 创建一个 saga 中间件 const sagaMiddleware = createSagaMiddleware() // 初始化 store const store = createStore(totalReducer, applyMiddleware(sagaMiddleware)) // 注册合并的 saga,必须要在初始化 store 之后 sagaMiddleware.run(RootSaga) ReactDOM.render( <React.StrictMode> {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */} <Provider store={ store }> <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
2、简化action与reducer
-
在之前代码的编写中,每次编写action都需要自己定义一个type方法类型,然后返回;定义reducer的时候也是每次需要通过switch进行判断,再去执行相应的方法。所以为了简化这两个步骤,用到一个库:
npm install redux-actions
。 -
来看关于reducer的代码:
-
Counter装啊提管理action方法的代码:
// src/Store/Actions/Counter.action.js // Counter 的 action 文件 // 导入 redux-actions 里面的 createAction 方法 import { createAction } from 'redux-actions' // +count 的函数 export const increment = createAction('increment') // -count 的函数 export const decrement = createAction('decrement')
注意: 导入
redux-actions
中的createAction()
方法,这个方法是用来进行创建action方法的,参数就是要创建的action的方法名 -
Counter状态管理reducer的代码:
// src/Store/Reducer/Counter.reducer.js // 自定义的一个 count 的 reducer // 导入 redux-actions 中的 handleActions 方法 import { handleActions as createReducer } from 'redux-actions' // 导入 action 中定义的两个方法 import { increment, decrement } from '../Actions/Counter.action' // 初始化一个默认的 state const defaultState = { count: 0 } // 使用 handleActions 方法创建 reducer const CounterReducer = createReducer({ // increment 事件,count + 1 [increment]: (state, action) => ({ count: state.count + 1 }), // decrement 事件,count - 1 [decrement]: (state, action) => ({ count: state.count - 1 }) }, defaultState) // 导出 export default CounterReducer
注意:
- 导入
redux-actions
中的handleActions()
方法,为了让其语义化更明显,所以我进行了起别名操作:handleActions as createReducer
。 handleActions()
方法有两个参数:- 参数一:定义的事件对象,每一个属性就是action文件中导出的方法,注意一定要用
[]
进行包裹。 - 参数二:定义的state数据。
- 参数一:定义的事件对象,每一个属性就是action文件中导出的方法,注意一定要用
- 导入
-
合并reducer:
// src/Store/Reducer/index.js // 合并 reducer // 导入 combineReducers 方法 import { combineReducers } from "redux"; // 导入 reducer import CounterReducer from './Counter.reducer' // 导出合并的 reducer export default combineReducers({ CounterReducer })
-
-
在主入口文件导入合并的reducer,进行注册store:
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; // 导入 redux import { createStore } from 'redux' // 导入 react-redux 的 Provider 模块 import { Provider } from 'react-redux' // 导入 ContentReducer import totalReducer from './Store/Reducer' // 初始化 store const store = createStore(totalReducer) ReactDOM.render( <React.StrictMode> {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */} <Provider store={ store }> <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
-
来看一下主入口页面
App.jsx
文件中的代码:// src/App.jsx // redux 工作流程梳理 import { Component } from 'react'; // 导入 connect 函数 import { connect } from "react-redux"; // 导入 bindActionCreators import { bindActionCreators } from "redux"; // 导入 Counter reducer 里面的方法 import * as CounterActions from "./Store/Actions/Counter.action"; class App extends Component { render() { console.log(this.props); return ( <div> <button onClick={ this.props.decrement }>-1</button> <span> { this.props.count } </span> <button onClick={ this.props.increment }>+1</button> </div> ) } } // 获取 store 中的数据 const mapStateToProps = (state) => ({ count: state.CounterReducer.count }) // 自动触发 action 的函数 const mapActionsToProps = (dispatch) => ({ ...bindActionCreators(CounterActions, dispatch) }) export default connect(mapStateToProps, mapActionsToProps)(App);
-
来看看页面效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cbonKD8-1654949415635)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b75323f838c04656a54ef97e3ac5030a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image)]