React 可预测状态容器 redux+saga

本贴最后更新于 1963 天前,其中的信息可能已经斗转星移

Redux 是一个 react 的数据仓库,学过 vuex 的同学可能比较熟悉,使用起来和 vuex 相似,但是配置极为麻烦,异步还需要配合 saga 实现,刚开始学这个东西时有点学蒙,现在整理以下

  • 此论坛发帖记得在代码块写上使用语言,因为没有代码块检测,我看不少人 的代码块都是纯白

安装 cnpm i redux react-redux
先来看一下 redux 在单文件中的使用

<!doctype html> <html> <head> <meta charset="UTF-8"> <title>Redux</title> </head> <script src="https://cdn.bootcss.com/redux/4.0.4/redux.min.js"></script> <script type="text/javascript"> //reducer 是一个箭头函数,传入 state (数据) 和 action(动作) //这个函数就是一个可预测状态容器,把可能发生的动作提前设定 var reducer=(state={idx:0},action)=>{//左边的={idx:0}是默认值 if(action.type=='add'){ //纯函数,不能更改 state 的值,而是直接返回新的对象 return { ...state, idx:state.idx+1 } } //默认返回 state 不 返回 redux 没办法获取并储存 state return state } //使用刚刚自己设定的容器创建 store var store = Redux.createStore(reducer) //dispatch 执行动作,传入一个对象 store.dispatch({type:'add'}); console.log(store.getState().idx) </script> <body> </body> </html>

解析:Redux 是使用自己定义的函数创建储存器,这就完成了 redux 这个仓库的创建,没有难度,建议码一遍这个函数的结构,会很清晰
之后使用 getState() 获取,dispatch() 执行 action

redux 在 react 中的使用

jsqReducer.js
export default (state={idx:10},action)=>{ if(action.type="ADD"){ return { ...state, idx:state.idx+1 }; } return state; }

如果有很多仓库,一个一个引入极为不便于管理,可以用内置函数合并这些函数为一个仓库,之后使用键名访问

合并自己刚刚定义的函数 Reducer

Reducers.js
import {combineReducer} from 'redux' import jsqReducer from './jsqReducer.js' export default combineReducer({ /*万能加一减一,右边是另一个文件暴露的函数*/ jsqReducer: jsqReducer })

import {createStore} from 'redux'
创建 Store 仓库,使用了刚刚合并的 Reducers

import Reducers from 'Reducers.js' const store = createStore(Reducers)

import {Proverd} from 'react-redux'
提供器 包裹 react render 内的标签

//包裹,完整代码在下方 <Provider store={store}> <App/> </Provider>

index.js

import React from 'react' import ReactDOM from 'react-dom' import {createStore} from 'redux' import {Provider} from 'react-redux' import Reducers from './reducers/Reducers' import App from './app' /*创建 store*/ const store = createStore(Reducers) console.log(store.getState().jsqReducer) ReactDOM.render( /*使用 recat-redux 组件包装自己的组件,并传入 store*/ <Provider store={store}> <App/> </Provider> , document.getElementById('app') )

代码段不支持 jsx 所以颜色很糊,建议复制

#####非简化装饰器
装饰器用于重包装自己的组件,并把 props 改写传入
import {connect} from 'react-redux'

import React, { Component } from 'react' import { connect } from 'react-redux' import { Button } from 'antd'//antd 是 ui 组件不用管 class app extends Component { render() { return ( <div> //直接 props 调用 <h1>{this.props.idx}</h1> <Button onClick={() => { //直接 props 调用 this.props.dispatch({ 'type': 'ADD' }) }}>按我加一</Button> </div> ) } } //装饰器,可以加装 babel 使用更高级写法 export default connect( //传入state (state) => ({ idx: state.jsqReducer.a }) , //传入 dispatch 函数 (dispatch) => ({ dispatch }) )(app) //两个括号 connect(函数,函数)(app)

至此已经完成了一个 redux 的封装和使用,如果要使用异步,就要再加装一个 saga 实现

saga 解决异步问题

npm install --save redux-saga npm install --save-dev @babel/plugin-proposal-decorators

redux-saga 就是 saga 本身,而 babel 翻译器是对装饰器语法的支持 类似 java 的装饰器,在函数上方 书写 @connect 这种以 @ 开头的语法简化上方 redux 丑陋的传值方式
####装饰器的 connect

import React, { Component } from 'react'; import { connect } from 'react-redux'; import { Table } from 'antd'; @connect( (state) => ({ page: state.escReducer.page, }), (dispatch) => ({ dispatch }) ) export default class App extends Component { ....

####Saga 实现和执行顺序
#####Saga 会拦截你的 dispatch 请求,优先执行 reducer 的文件,然后再执行你的中间件 saga 文件内的 takeEvery('被拦截的 type',函数:拦截后执行)

#####rootSaga.js

import {takeEvery,put} from 'redux-saga/effects' import Axios from 'axios' export default function * (){ yield takeEvery('LOADDATA',function *(){ yield console.log('rootSage的LOADDATA被执行') const {results} = yield Axios.get('http://xxxxx/api').then(data=>data.data) yield put({'type':'CHANGRESULTS',result}) }) }

#####index.js

import React from 'react' import ReactDOM from 'react-dom' import {createStore,applyMiddleware} from 'redux' import createSagaMiddleware from 'redux-saga' import {Provider} from 'react-redux' import Reducers from './reducers/Reducers' import App from './app' import rootSaga from './sagas/rootSaga' //创建中间件 const saga = createSagaMiddleware() //创建仓库 const store = createStore(Reducers,applyMiddleware(saga)) //运行拦截器 saga.run(rootSaga) ReactDOM.render( //使用 recat-redux 组件包装自己的组件,并传入 store <Provider store={store}> <App/> </Provider> , document.getElementById('app') )

#####App.js 你的组件

componentDidMount(){ //这个函数在 takeEvery 处被拦截了 this.props.dispatch({'type':'LOADDATA'}) //正常执行到 reducer this.props.dispatch({'type':'CHANGRESULT'}) }

执行 顺序
#####erpReducer.js

export default (state={ page:1, results:[] },actions)=>{ if (actions.type=="CHANGRESULTS"){ console.log('reducer内的CHANGRESULTS被调用') return { ...state, results:actions.results } }else if (actions.type=="LOADDATA"){ console.log('reducer内的 LOADDATA 被调用') return { ...state, results:actions.results } } return state; }

因为 reducer 的监听会先调用所以一般我们都会让监听的字符串不相同
在 saga 中监听 loaddata 获取到数据后再使用

put({type:'changeResults',result}) //重新发送一条 dispatch 到 reducer

其实 redux 和 saga 都被 dva 废了 但必要的了解少不了,比较很多公司还是要用的

  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    246 引用 • 1338 回帖
3 操作
Devourgod 在 2019-12-29 11:11:46 更新了该帖
Devourgod 在 2019-12-29 11:10:29 更新了该帖
Devourgod 在 2019-12-28 15:34:02 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...