原文收录在github 库房 (包含小demo) github地址:点这里

一、Redux

1. readux的概述

通用的状况办理辅佐东西,习惯上咱们可以结合ReactJs 来运用,在组件化开发5 H $ Z L }过程中,组件的数据状况不得不集中化办理,这也是咱们运用Redud p /x的原因之一,是一个数据的容器。习惯上咱们称之M m e $ v Zjs库

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)

小结

通过一个简略的事例,咱们知道一个简易的流程:

  1. 首先构建一个action 回来一个对象必须有type特点
  2. 构建reducer 呼应action t通过return 把数据传回sW h P g ( 0 8 (tore
  3. 运用redux这个库t * p来创立一个store 传递写好的reducer
  4. 运用的$store.subscribe(M ; i W) 注册监听
  5. 可以通过store.getState() 取值

二 、L } $ Q 9React-Redux

那在如上咱们运用的redux 这个库看起来是没有问题,可是

  • 首先要导入store
  • 然后注册监听
  • 然后组件销毁的时分,咱们撤销监听

这一i P I z k 6 [波流的操作在每个组件都要走一遍,显然是十分繁琐和重复的,这就需求看谁能不能帮帮我,这便是react-redux 如果需求把redux整合到reactf 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 处理数据

四、思考

  1. Hooks API ,也便是函数式的组件怎样5 l % ^ V –监听页面数据的改变 ,然后履行改写?
  2. redux-saga 中的辅佐函数 takeEveryo a Y ~ : r H d takeLatest throttle: } y c =底层有什么区别?

感谢你看到这,无妨给个星星,感谢