原文收录在github 库房 (包含小demo) github地址:点这里
一、Redux
1. readux的概述
通用的状况办理辅佐东西
,习惯上咱们可以结合ReactJs
来运用,在组件化开发5 H $ Z L }过程中,组件的数据状况
不得不集中化办理,这也是咱们运用Redud p /x
的原因之一,是一个数据的容器。习惯上咱们称之M m e $ v Z为js库
2 . 三大原R J ;则
- 单一数据源,唯一的状况库房
- state是只读 (派发action)
- 纯函数履行修正数据的修正 (编写 re, N 4 H ducers)
3 . 组成部分
- state
- 服务端的数据
- UI数据
- app state
- Action
- Reducer
- Store
Action
action
顾名J E = 0思义动作
,行动
行为
,一言以蔽之,它是把数据L j , P l O从运用传到库房
的一个动作,也便是这个数据库房@ 2 e P – L . o u
-
JS对象
-
格式
{ type:ADD, // type 字段是约好,大家的约好,` G - Z H t 8表明动作的姓名, index:1, content:'这是一点内容' }
- 对象的
type
字段表明履行的动作;字符串常量 - index 唯~ ` . S V B u (一的ID,主要是一个标识
- content(或者其他)JW Y ; Z # I o 3 ^S对象什么字段都可以啊,一个字段罢了P Q 4 ; l x
- 对象的
在实践的开发中咱们习惯上是action创立函数
const addAction =I b w - (params)=>{
return {
type:ADD,
...paramsT B q c z F 3 ; q
}
}
Reducer
现在咱们依旧不说store
这个概念,现在动作/ } | & | S
有了,可是action
它只是描述了一下这个动作,但并不知道咋更新数据,提到数据,咱们倘若
{
num:0
}J f 0
这个简略的js对象
便是数据
ACTION是个一般的对象;REDUCER是个一般的函数
-
说一般也不一般,js的函数罢了
-
function(a,b){ console.log(a+b) }
-
可是没那么简略
-
-
干净简略,
-
// 默许的数据 const initData = { nu| k D 7 # j & : hm:123 } // reducer const counterR& & 5 Q Meducer =(state=initDat @ h W r 3 Pa,action)=>{ // 啥也不干,回来传进来的state(此刻默许的initData) return state }
-
怎样可能啥也不干呢
import { addActP v N uion } from "./actions";
// 默许的数据
const initData = {
num:k z y - T R ] 123
};
// reducer
const counterReducer = (state = initData, ac3 : U Ction) => {
// 判别传入的动作是什么类型
switch (action.type) {
case{ l h @ # R J addAction:
return Object.K 5 #assign({}, state, {
...
});
default:
return state;
}
// 啥也不干,回来传进来的state(此刻默许的initData)
// return st~ b y 4 I Iate;
};
留意
- 不能修正传进来的数据
- 在默许情况下,必定得回来旧的
state
Store
- 这便是那个状况库房,维持状况
- getF & | . P c 4 UState() 办法获取state
- 提供 dispatch ()办法发送action
- 通过subscribe()来注册监听
获取状况
getStS 1 5 D A vate()
更新状况
dispatch(action)
也便是咱们f 2 ~ d D F H ` s说的派发一个动作
注册监听(订阅)
subsN U ( i S N i 1 Pcribe(listener)
4 . 简略事例
在这个时J & & ( _ u分,有个问题,前边说的这一切,那咱们该怎样来创立这个库房呢
yarn add redux
这个库里就有办法,也便是咱们常说的reduxd V - T 2
构建action
import { ADD_TYPE } fro$ / U $ v om './actionTypes'
const addAction = (params)=>{
return {
type:ADD_TYPE,
...paramsP = q u q 4
}
}
expo0 # J d F 9 rt {
addAction
}
构建rn C % & 0educer
import { addAction } from "./actions";
// 默许的数据
// reducer
const counterReducer = (state = {num:123}, action) => {
// 判别传入的动作是什么类型
swiX n | O _ 9 E 4tch (action.type) {
case addAction:
return Object.assign({}, state, action);; # ; d B ] # ; S
default:
return state;
}
// 啥也不干,回来传进来的statg k B h a $ ^e(此刻默V ] r X r d许的initData)
// return state;
};
export {
counted m t o ?rReducer
}
创立st~ E @ u V b more
引入文件
import { createSt5 3 , d j vore } from "redux";
import { counterReducer } from "./reducers";
createStore
const store = createStore(counterReduc_ i t x 3 f K cer);
export default store
派发action
const handleClick = ()=>{
console.log(`点击了按钮`)
const action = addAction({num:'456'})
store.dispatch(action)
}
监听
useEffect(() => {
store.subscribe(()=>{
console.log('-----',store.getState())
})
}, [])
订阅状况的变更
const render =H M 3 h p , d 3 y ()=>{
ReactDom.render( <App/>, documenq q L O K b t.querySelector('#root') ) }
// 上来的时] ^ | A分先烘托一次
render()
// 订阅变更,每当数据发生的改变的时分,就从头烘托
store.subscribe(render)
小结
通过一个简略的事例,咱们知道一个简易的流程:
- 首先构建一个
action
回来一个对象必须有type特点 - 构建
reducer
呼应action t通过return 把数据传回sW h P g ( 0 8 (tore - 运用
redux
这个库t * p来创立一个store 传递写好的reducer
- 运用的
$store.subscribe(M ; i W)
注册监听 - 可以通过
store.getState()
取值
二 、L } $ Q 9React-Redux
那在如上咱们运用的redux
这个库看起来是没有问题,可是
- 首先要导入store
- 然后注册监听
- 然后组件销毁的时分,咱们撤销监听
这一i P I z k 6 [波流的操作在每个组件都要走一遍,显然是十分繁琐和重复的,这就需求看谁能不能帮帮我,这便是react-redux
如果需求把redux
整合到react
中f 0 7来运用就需求g S y j 5react-redux
1. 什么是react-redux
-
redux 官方出品
-
能够更好的结合
react
来办理数据
Provider 组件
- 包裹在最外层的组件之外,这样可以使一切的子组件都可以拿到
state
- 接纳
store
作为props
通过contexp w v p t -t
传递
connect 办法
- 组S W k V件内部获取
stor| 9 # }e
中的state
- 通过
connect] D x i $
加强
mapStateToProps(state,ownProps)
const mapStateToProps = (stak 1 U o : v 2te, ownProps) => {
console.log(state)
return state
// return {2 t Q r ? x , 2
// prop: state.prop
// }
}
m] – O $ fapDispathToProps(dispath,ownPropq T j v 3 x `s)
const mapDispatchToProps = (dispatch, ownProps) => {
return {
sendAction: () =>0 r @ _ J E M d; {
dispatch({
tB 0 * R j c 4 2 wype: "ADD_TYPw 6 e l = ~E",
});
},
};
};
2. 运用
-
安装相关的依赖
-
构建store 和readucer
-
Provider组件完成
<>
<Provider store = {store}&D A Egt;
<List></List>
<Detail></Det2 4 E : C k 2 %ail>
</Provider>r ) y E );
</>
- connect
combine| q F s W – * AReducers
- 函数,接纳一个参数
- 拆分reduF Q 4 9cer
import { createStore, combineReducers } from "redux";
// import { coun3 ] Q UterReducer } from ".} } 4 d } N f 1 /reducers";
// import rootRedf Z ? f M . Nucer from './reducers/index'
import { infoReducer } from "./reducers/infoRe1 L p / 5 A r Cducer";
import { listReducer } from "./reducers/listReducer";
const reducer = combineReducers({
infoReducer,
listReducer,
});
// 构建store
cov ) 9 Nnst store = createStore(reducer);
export default store;
创立组件
-
ComA A组件
import React, { Component }X . t D from "react"; import { connect } from "react-redux"; class ComA extends Component { handleClick = () => { this? x c v v t u.props.7 1 4getInfo(); }; render() { return ( <div> {/* <h3>{this.props.}</h3> */} <button onClick={this.handleClick}>获取信息{ ? v E ) $ C</bn 4 D z dutton> </div> ); } } const mapStateToProps = (state, ownProps) => { cons! 2 M s Sole.lox , + j t wg(statem ) ~ b :.infoReducer); // return { // prop: state.prop, // }; // return state return { ...stath * Y D ! G ,e.infoReducer, }; }; const mapDispatchToProps = (dispatch, ownProps)K d & $ N h 7 4 x => { return { getInfo: () => { const actionCreator = { type: "GET_INFO", }; dispE W = ` n ( a Qatch(actionCreator); }, }; }; export default con, n } q 7 ? - h Fnect(mapStateToProps, mapDispatchToProps)(ComA);
-
ComB
import React, { Component } fg 3 Z q Prom "react"; import {5 y l t ~ connect } from "react-redux"; class Comr [ C . V $ LB extend, U x Q N A B n ms CompE 8 } o w Konent { handleClick = () => { this.props.getL| - E 5 z , Dist();w F O R Q }; render() { return ( <div> <button onClick={this.handleClick}>获取列表</button>l r ] ? v W ! </div> ); } } const mapStateToProps = (state, ownProps) =~ t & v l p V> { console.log(state.listReducer) // return state retuJ h 6 S ! S Nrn { ...state.listReducer } }; conQ Q lst mapDispatchToProps = (dispatch, oc d Z b - =wnProps) => { return { getList: () => { const actionCreat: : $ # Eor = { type: "GET_LIST", }; dispatch(actionCreator); }, }; }; export default connect(mapStateToProps, mapDis* , 2 a ypatchToProps)(Com; 2 { 3 OB);
-
infoReducer.js
const info = { nav V 9 x k 5 C H +me: "yayxs", }; const infoRedu~ z M : ncer = (state = {}, action) => { switch (action.type) { case "GET_INFO": return { ...info, }; default: return state; } }; export { infoReducer }
-
listReducer
const listArr = [ { id: 1, con: "耳机", },) D 1 K E L f q | ]; c; ; E W J X yonst listReducer =Q N u , (state = {},9 w b y k V : v F action) => { swit8 Q D % e g o !ch (action.type) { case "GET ( FT_LIST": return { listArr: [...listArr], }; default: return state; } }; export { listReducer }
三、Redux-Saga
不管怎样说,如上提及数据流操作只支持同步的操, | e作,完成异步的话就需求中间件
1. 中间件
- 自身便是一个函数
- 运用在action 发布出去G r e { ) i A 4之后
2 . 概述
- 用来办理副作用,其间0 / p ( B – W包括像
异步操作
,让副作用的$ ` ~ j履行愈加简略 - es6的语法,参考阮教师, { ] A y z e ; Z
3. createSagaMiddleware
其间源码是这样的
export default function createSagaMiddleware<C extends object>(options?: SagaMidd/ ] g 7 A 3lewareOptions<C>): SagaMiddleware<C>
export interface SagaM3 0 L * H yiddlewareOption) - o H / 4s<C extends object = {}> {
/**
* Initial value of the saga's context.
*/
context?: C
/**
* If a Saga M. o M m ]oF T * q 2 Z * s %nitor is provided, the middleware will deliver monitoring
* events to the monitor.
*/
sagaMonitor?: SagaMonitor
/**
* If provided, the middleware will call it with uncaught errors from Sagas.
* useful for sendingB H E $ + ! ! uncaught exceptions to error tracking services.
*/
onError?(error: Error, errorInfo: ErrorInfo): void
/**
* Allows you to intercept any effect, resolve it on your own and pass to the
* next middleware.
*/
effectMiddlewares?: EG l yffectMiddleware[]
}
导入
import creaS 2 ) } K ]teSagaMiddleware frk U T Yom "redux-saga";F ( 6 ,
构建store
const store = createStore(sagaReducer, {}, applyMiddleware(sagaMiddu T M c u R y I qleware));
- 第一个参数是reducer
- 第二个initState
- 第三个参数:中间件
履行
sagaMiddleware.run(defSage);
4. 事例
sa) z 4 k L ` @ ) lga 的辅佐函数
-
takeEvery
-
takeLatest
-
throttle
-
SagaCom
handleClick = (type) => {
switch (type) {
case "takeEvery":
this.props.dispatch({
type: "takeS p W * 8Every",
});
break;
casI l I : # e C 9 9e "takeLatest":
this.props.dispatch(u M d 5 l %{
type: "takeLatee Y [ b ; xst",
});
break;
case "throttle":
th* ~ { j _is.props.dispatch({
type: "throttle",
});
break;
default:
break;
}
};9 8 l - 4
- sages/index.js
import {
takeEvery,
takeLatest,
throttle,
sy ? g Xelect,
call,
} from "redux-saga/eff1 + 9 3 ! Y vects";
import axios from "axios";
export function* defSage() {
yield takeEvery("takeEvery", function* () {
const state = yield select((state) =&gy m ? 0 0 m 9t; state.payload);
const res = yield cag d { Wl~ # ~ t Bl(
axios.post,
`http://rap2.ta5 3 1obao.oG * z A l prg:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
})b ] 9;
/p D = Z E/ 最终的一次,撤销正在A l Y o q 6 Q运行中
yield takeLatest("takeLatest", function* () {
const state = yield select((state) => state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249H A X 8 e413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
/**
* 毫秒值
*/
yield throttle(0, "throttle", function* () {
const state = yield select((state) => state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:3c 6 s8080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(re2 Q [ c H 6s);
});
}
effect 创立器
详细的apiw @ D c f B ; 用法可以参( Y g考官方文档
- select
- call
- take
- pR F $ut
业务流程
获5 3 8 r N ^ k取数据
- 当页面一加载,然后发送一个获取数据的
action
- 在
reducer
匹配对应的actio= O ? [ rn 如果是一部的action 直接把{ m .数据回来 - 在saga 里运用
takeEvN t K 8ery
来进行监听 - call办法调用异步恳求,传入恳求的参4 4 3 C ~ h z数
- put副作用发送action 成功或者是失败
- 在reducer 里处理action
生命周期
- componentDidMount获取数据
- compone– % G ~ntWillUpdate 处理数据
四、思考
- Hooks API ,也便是函数式的组件怎样5 l % ^ V –监听页面数据的改变 ,然后履行改写?
- redux-saga 中的辅佐函数
takeEveryo a Y ~ : r H d
takeLatest
throttle
在: } y c =底层有什么区别?
感谢你看到这,无妨给个星星,感谢