1.外围概念

1.什么是Redux?

Redux是一个治理状态(数据)的容器,提供了可预测的状态治理

2.什么是可预测的状态治理?

数据 在什么时候, 因为什么, 产生了什么扭转,都是能够管制和追踪的,咱们就称之为预测的状态治理

3.为什么要应用Redux?

React是通过数据驱动界面更新的,React负责更新界面, 而咱们负责管理数据
默认状况下咱们能够在每个组件中治理本人的状态, 然而当初前端应用程序曾经变得越来越简单
状态之间可能存在依赖关系(父子、共享等),一个状态的变动会引起另一个状态的变动
所以当应用程序简单的时候, 状态在什么时候扭转,因为什么扭转,产生了什么扭转,就会变得十分难以管制和追踪
所以当应用程序简单的时候,咱们想很好的治理、保护、追踪、管制状态时, 咱们就须要应用Redux

4.Redux核心理念

通过 store 来 保留数据
通过 action 来 批改数据
通过 reducer 来 关联 store 和 action

2.三大准则

1.Redux三大准则

繁多数据源
整个应用程序的state只存储在一个 store 中
Redux并没有强制让咱们不能创立多个Store,然而那样做并不利于数据的保护
繁多的数据源能够让整个应用程序的state变得不便保护、追踪、批改
State是只读的
惟一批改State的办法肯定是触发action,不要试图在其余中央通过任何的形式来批改State
这样就确保了View或网络申请都不能间接批改state,它们只能通过action来形容本人想要如何批改stat;
这样能够保障所有的批改都被集中化解决,并且依照严格的程序来执行,所以不须要放心race condition(竟态)的问题;
应用纯函数来执行批改
通过reducer将 旧state和 action分割在一起,并且返回一个新的State:
随着应用程序的复杂度减少,咱们能够将reducer拆分成多个小的reducers,别离操作不同state tree的一部分
然而所有的reducer都应该是纯函数,不能产生任何的副作用

2.什么是纯函数

返回后果只依赖于它的参数,并且在执行过程外面没有副作用

// 纯函数function sum(num1, num2){    return num1 + num2;}// 非纯函数let num1 = 10;function sum(num2){    return num1 + num2;}// 纯函数const num1 = 10;function sum(num2){    return num1 + num2;}

3.根本应用

筹备工作
创立 demo 目录
cd demo
npm init -y #初始化一个node我的项目
npm install --save redux #装置redux
redux的应用
store.subscribe() #监听函数(一旦 state 发生变化,就主动执行这个函数)

store.getState() #获取以后的 state

store.dispatch() #派发 action(用于批改state)

// 导入reduxconst redux = require('redux');// 1.定义一个状态(数据)let initialState = {    count: 0}// 2.利用store保留状态const store = redux.createStore(reducer);// 3.利用action批改状态const addAction = {type:'ADD_COUNT', num: 1};const subAction = { type: 'SUB_COUNT', num: -1};// 4.利用reduce关联store与actionfunction reducer(state = initialState, action){    switch(action.type){        case 'ADD_COUNT':            return { count: state.count + action.num};        case 'SUB_COUNT':            return { count: state.count + action.num };        default:            return state;    }}
// 在组件中如何应用?// 1.监听状态的扭转store.subscribe(()=>{    console.log(store.getState());})// 2.获取Store中存储的状态console.log(store.getState());// 3.批改Store中存储的状态store.dispatch(subAction);// console.log(store.getState());

以后代码存在的问题
1.store、action、reducer代码都写在一个文件中,不利于保护
2.action和reducer中都是应用字符串,来指定和判断操作类型, 写错不报错
3.action中的操作写死了,不够灵便
优化

const redux = require('redux');// 定义常量const ADD_COUNT = 'ADD_COUNT';const SUB_COUNT = 'SUB_COUNT';// 定义一个状态let initialState = {    count: 0};// 利用store来保留状态(state)const store = redux.createStore(reducer);// 利用action来批改状态// const addAction = {type:ADD_COUNT, num: 1};// const subAction = {type:SUB_COUNT, num: -1};const addAction = (num)=>{    return {type:ADD_COUNT, num: num};};const subAction = (num)=>{    return {type:SUB_COUNT, num: num};};// 利用reducer将store和action串联起来function reducer(state = initialState, action) {    switch (action.type) {        case ADD_COUNT:            return {count: state.count + action.num};        case SUB_COUNT:            return {count: state.count - action.num};        default:            return state;    }}// 在组件中如何监听状态的扭转?store.subscribe((a)=>{    console.log(store.getState());});// 在组件中如何从Store中获取存储的状态?console.log(store.getState());// 在组件中如何批改Store中存储的状态?// store.dispatch(addAction(5));store.dispatch(subAction(5));// console.log(store.getState());

4.联合 React

  • npm install --save redux
  • 在src目录下,新建store目录

    store/constants.js

// 定义常量export const ADD_COUNT = 'ADD_COUNT';export const SUB_COUNT = 'SUB_COUNT';

store/store.js

import {createStore} from 'redux';import reducer from './reducer';// 利用store来保留状态(state)const store = createStore(reducer);export default store;

store/action.js

import {    ADD_COUNT,    SUB_COUNT} from './constants';// 利用action来批改状态// const addAction = {type:ADD_COUNT, num: 1};// const subAction = {type:SUB_COUNT, num: -1};export const addAction = (num)=>{    return {type:ADD_COUNT, num: num};};export const subAction = (num)=>{    return {type:SUB_COUNT, num: num};};

store/reducer.js

import {    ADD_COUNT,    SUB_COUNT} from './constants';// 定义一个状态let initialState = {    count: 666};// 利用reducer将store和action串联起来function reducer(state = initialState, action) {    switch (action.type) {        case ADD_COUNT:            return {count: state.count + action.num};        case SUB_COUNT:            return {count: state.count - action.num};        default:            return state;    }}export default reducer;

App.js

import React from 'react';import store from './store/store';import {addAction, subAction} from './store/action';class App extends React.PureComponent{    constructor(props){        super(props);        this.state = {            // 1.getState():获取存储的状态(数据)            count: store.getState().count        }    }    // componentDidMount:当组件被挂载时(曾经实现渲染),react会主动调用该办法    componentDidMount() {        // 3.subscribe():监听状态的扭转        store.subscribe(()=>{            // 将批改后的数据从新存储到state中            this.setState({                count: store.getState().count            })        })    }    // componentWillUnmount:当组件被卸载时,react会主动调用该办法    componentWillUnmount() {        // 4.unsubscribe():移除监听状态的扭转事件        store.unsubscribe();    }render(){    return(        <div>            <p>{this.state.count}</p>            <button onClick={()=>{this.btnClick()}}>减少</button>        </div>    )}btnClick(){    // 2.dispatch():批改Store中存储的状态    store.dispatch(addAction(5));}}export default App;

存在问题:
1.冗余代码太多, 每次应用都须要在构造函数中获取
2.每次应用都须要监听和勾销监听
3.操作store的代码过于扩散
如何优化:
如何解决冗余代码太多问题

应用 React-Redux

什么是 React-Redux
React-Redux是Redux官网的绑定库,可能让咱们在组件中更好的读取和操作Redux 保留的状态
装置react-reduct: npm install react-reduct
index.js

import ReactDOM from 'react-dom';import React from 'react';import App from './App';import { Provider } from 'react-redux'import store from './store/store';ReactDOM.render(    // 只有利用Provider将先人组件包裹起来    // 并且通过Provider的store属性将Redux的store传递给Provider    // 那么就能够在所有后辈中间接应用Redux了    <Provider store={store}>        <React.StrictMode>            <App/>        </React.StrictMode>    </Provider>    , document.getElementById('root'));

components/Home.js

import React from 'react';import { connect } from 'react-redux';import {addAction, subAction} from '../store/action';class Home extends React.PureComponent{    render(){        return (            <div>                 {/* 3.通过props来应用redux中保留的数据 */}                <p>{this.props.count}</p>                <button onClick={()=>{this.props.increment()}}>递增</button>            </div>        )    }}// 1.mapStateToProps办法:通知React-Redux, 须要将store中保留的哪些数据映射到以后组件的props上const mapStateToProps = (state)=>{    return{        count: state.count    }}// 2.mapDispatchToProps办法:通知React-Redux, 须要将哪些派发的工作映射到以后组件的props上const mapDispatchToProps = (dispatch)=>{    return{        increment(){            dispatch(addAction(1));        }    }}// 4.connect:关联Home组件与mapStateToProps和mapDispatchToProps办法export default connect(mapStateToProps, mapDispatchToProps)(Home);

5.Redux 解决网络申请

前提
1.搭建一个Egg我的项目
2.疾速启动Egg我的项目( npm run dev )
3.输出:localhost:7001/info 即可拜访页面

store/constants.js

// 定义常量export const ADD_COUNT = 'ADD_COUNT';export const SUB_COUNT = 'SUB_COUNT';export const CHANGE_INFO = 'CHANGE_INFO';

store/store.js

import {createStore} from 'redux';import reducer from './reducer';// 利用store来保留状态(state)const store = createStore(reducer);export default store;store/action.jsimport {    ADD_COUNT,    SUB_COUNT,    CHANGE_INFO} from './constants';// 利用action来批改状态// const addAction = {type:ADD_COUNT, num: 1};// const subAction = {type:SUB_COUNT, num: -1};export const addAction = (num)=>{    return {type:ADD_COUNT, num: num};};export const subAction = (num)=>{    return {type:SUB_COUNT, num: num};};export const changeAction = (info)=>{    return {type:CHANGE_INFO, info: info};};

store/reducer.js

import {    ADD_COUNT,    SUB_COUNT,    CHANGE_INFO} from './constants';// 定义一个状态let initialState = {    count: 666,    info: {}};// 利用reducer将store和action串联起来function reducer(state = initialState, action) {    switch (action.type) {        case ADD_COUNT:            return {...state, count: state.count + action.num};        case SUB_COUNT:            return {...state, count: state.count - action.num};        case CHANGE_INFO:        return {...state, info: action.info};        default:            return state;    }}export default reducer;

components/About.js

import React from 'react';import { connect } from 'react-redux';import { addAction, changeAction } from '../store/action';class About extends React.PureComponent {    // componentDidMount:曾经挂载(渲染实现)时,react会主动调用该办法    componentDidMount() {        // 发送GET申请        fetch('http://localhost:7001/info')            .then((response) => {                // 转换为json格局                return response.json();            })            .then((data) => {                // console.log(data);                this.props.changeInfo(data);            })            .catch((error) => {                console.log(error);            })    }    render() {        return (            <div>                {/* 3.通过props来应用redux中保留的数据 */}                <p>{this.props.count}</p>                <button onClick={() => { this.props.increment() }}>递增</button>                <p>{this.props.info.name}</p>                <p>{this.props.info.age}</p>            </div>        )    }}// 1.mapStateToProps办法:通知React-Redux, 须要将store中保留的哪些数据映射到以后组件的props上const mapStateToProps = (state) => {    return {        count: state.count,        info: state.info    }}// 2.mapDispatchToProps办法:通知React-Redux, 须要将哪些派发的工作映射到以后组件的props上const mapDispatchToProps = (dispatch) => {    return {        increment() {            dispatch(addAction(1));        },        changeInfo(info) {            dispatch(changeAction(info));        }    }}// 4.connect:关联Home组件与mapStateToProps和mapDispatchToProps办法export default connect(mapStateToProps, mapDispatchToProps)(About);

6.Redux-thunk 中间件

优化:将 异步申请 封装到 Redux 中

1.以后保留异步数据存在的问题
异步数据既然要保留到Redux中, 所以获取异步数据也应该是Redux的一部分
所以获取异步数据的代码应该放到Redux中, 而不是放到组件生命周期办法中
2.如何在Redux中 获取网络数据
应用 redux-thunk 中间件
3.redux-thunk 作用
默认状况下 dispatch 只能接管一个对象
应用 redux-thunk 能够让 dispatch 除了能够接管一个对象以外, 还能够接管一个函数
通过 dispatch 派发一个函数的时候可能去执行这个函数, 而不是执行 reducer 函数
4.redux-thunk 如何应用
装置 redux-thunk
npm install redux-thunk
在创立 store 时利用 redux-thunk 中间件
在 action 中获取网络数据
在组件中派发 action

store/store.js

import { createStore, applyMiddleware } from 'redux';import reducer from './reducer';// 1.导入thunkMiddleware中间件import thunkMiddleware from 'redux-thunk';// 2.利用thunkMiddleware中间件// 在创立store之前, 通过applyMiddleware办法, 通知Redux须要利用哪些中间件const storeEnhancer = applyMiddleware(thunkMiddleware);// 利用store来保留状态(state)const store = createStore(reducer, storeEnhancer);export default store;

store/action.js

import {    ADD_COUNT,    SUB_COUNT,    CHANGE_INFO} from './constants';// 利用action来批改状态// const addAction = {type:ADD_COUNT, num: 1};// const subAction = {type:SUB_COUNT, num: -1};export const addAction = (num) => {    return { type: ADD_COUNT, num: num };};export const subAction = (num) => {    return { type: SUB_COUNT, num: num };};export const changeAction = (info) => {    return { type: CHANGE_INFO, info: info };};// 定义getUserInfo办法,在这个办法中发送网络申请并派发工作export const getUserInfo = (dispatch, getState)=>{    // 发送GET申请    fetch('http://localhost:7001/info')        .then((response) => {            // 转换为json格局            return response.json();        })        .then((data) => {            // console.log(data);            // dispatch:派发工作            dispatch(changeAction(data));        })        .catch((error) => {            console.log(error);        })}

components/About.js

import React from 'react';import { connect } from 'react-redux';import { addAction, getUserInfo } from '../store/action';class About extends React.PureComponent {    // componentDidMount:曾经挂载(渲染实现)时,react会主动调用该办法    componentDidMount() {       // 调用changeInfo办法       this.props.changeInfo();    }    render() {        return (            <div>                {/* 3.通过props来应用redux中保留的数据 */}                <p>{this.props.count}</p>                <button onClick={() => { this.props.increment() }}>递增</button>                <p>{this.props.info.name}</p>                <p>{this.props.info.age}</p>            </div>        )    }}// 1.mapStateToProps办法:通知React-Redux, 须要将store中保留的哪些数据映射到以后组件的props上const mapStateToProps = (state) => {    return {        count: state.count,        info: state.info    }}// 2.mapDispatchToProps办法:通知React-Redux, 须要将哪些派发的工作映射到以后组件的props上const mapDispatchToProps = (dispatch) => {    return {        increment() {            dispatch(addAction(1));        },        changeInfo() {            // dispatch(changeAction(info));            // 留神点: 默认状况下dispatch办法只能接管一个对象            //        如果想让dispatch办法除了能够接管一个对象以外, 还能够接管一个办法            //        那么咱们能够应用 redux-thunk 中间件            // redux-thunk中间件作用:            // 让dispatch办法能够接管一个函数, 让咱们在通过dispatch派发工作的时候去执行咱们传入的办法            // 在dispatch派发工作时,间接执行getUserInfo办法            dispatch(getUserInfo);        }    }}// 4.connect:关联Home组件与mapStateToProps和mapDispatchToProps办法export default connect(mapStateToProps, mapDispatchToProps)(About);

7.Redux-saga 中间件

1.什么是 Redux-saga

redux-saga 和 redux-thunk 一样,是一个 Redux 中获取存储异步数据的中间件
redux-saga 能够间接拦挡 dispatch 派发的 action, 从而实现在执行 reducer 之前执行 一些其它操作

2.如何应用 Redux-saga

1.装置 Redux-saga
npm install redux-saga
2.在创立 store 时,利用 redux-thunk 中间件
3.在生成器函数中获取网络数据
4.在组件中派发 action
store/store.js

import { createStore, applyMiddleware } from 'redux';import reducer from './reducer';import mySaga from './saga';/*留神点: 如果导入的是redux-thunk, 那么返回给咱们的是一个中间件对象       如果导入的是redux-saga, 那么返回给咱们的是一个用于创立中间件对象的办法* */  // import thunkMiddleware from 'redux-thunk';// 1.导入中间件办法import createSagaMiddleware from 'redux-saga';// 2.通过createSagaMiddleware办法,创立saga中间件对象const sagaMiddleware = createSagaMiddleware();// 3.利用sagaMiddleware中间件// 在创立store之前, 通过applyMiddleware办法, 通知Redux须要利用哪些中间件const storeEnhancer = applyMiddleware(sagaMiddleware);// 利用store来保留状态(state)const store = createStore(reducer, storeEnhancer);/*留神点: 如果是redux-thunk, 那么在创立store的时候指定完中间件即可       如果是redux-saga, 那么除了须要在创立store的时候指定中间件以外, 还须要手动的调用中间件的run办法才行* */  // 4.拦挡 action  // 利用传入的生成器通知redux-saga, 须要拦挡哪些dispatch派发的action  sagaMiddleware.run(mySaga);export default store;

store/constants.js

// 定义常量export const ADD_COUNT = 'ADD_COUNT';export const SUB_COUNT = 'SUB_COUNT';export const CHANGE_INFO = 'CHANGE_INFO';export const GET_USER_INFO = 'GET_USER_INFO';store/soga.jsimport { takeEvery, put } from 'redux-saga/effects'import { GET_USER_INFO } from './constants';import { changeAction } from './action';function *myHandler() {    // 获取网络数据    const data = yield fetch('http://127.0.0.1:7001/info')        .then((response) => {            return response.json();        })        .catch((error) => {            console.log(error);        });    // console.log(data);    // 保留获取到的数据,相当于 store.dispatch(changeAction());    yield put(changeAction(data));}function *mySaga() {    // 第一个参数:指定须要拦挡的action类型    // 第二个参数:指定拦挡到这个类型的action之后交给谁来解决    yield takeEvery(GET_USER_INFO, myHandler)}export default mySaga;

store/action.js

import {    ADD_COUNT,    SUB_COUNT,    CHANGE_INFO,    GET_USER_INFO} from './constants';// 利用action来批改状态export const addAction = (num) => {    return { type: ADD_COUNT, num: num };};export const subAction = (num) => {    return { type: SUB_COUNT, num: num };};export const changeAction = (info) => {    return { type: CHANGE_INFO, info: info };};export const getUserInfo = () => {    return { type: GET_USER_INFO }}

store/reducer.js

import {    ADD_COUNT,    SUB_COUNT,    CHANGE_INFO} from './constants';// 定义一个状态let initialState = {    count: 666,    info: {}};// 利用reducer将store和action串联起来function reducer(state = initialState, action) {    switch (action.type) {        case ADD_COUNT:            return { ...state, count: state.count + action.num };        case SUB_COUNT:            return { ...state, count: state.count - action.num };        case CHANGE_INFO:            return { ...state, info: action.info };        default:            return state;    }}export default reducer;

components/About.js

import React from 'react';import { connect } from 'react-redux';import { addAction, getUserInfo } from '../store/action';class About extends React.PureComponent {    // componentDidMount:曾经挂载(渲染实现)时,react会主动调用该办法    componentDidMount() {        // 调用changeInfo办法       this.props.changeInfo();    }    render() {        return (            <div>                {/* 3.通过props来应用redux中保留的数据 */}                <p>{this.props.count}</p>                <button onClick={() => { this.props.increment() }}>递增</button>                <p>{this.props.info.name}</p>                <p>{this.props.info.age}</p>            </div>        )    }}// 1.mapStateToProps办法:通知React-Redux, 须要将store中保留的哪些数据映射到以后组件的props上const mapStateToProps = (state) => {    return {        count: state.count,        info: state.info    }}// 2.mapDispatchToProps办法:通知React-Redux, 须要将哪些派发的工作映射到以后组件的props上const mapDispatchToProps = (dispatch) => {    return {        increment() {            dispatch(addAction(1));        },        changeInfo() {            // 在dispatch派发工作时,间接执行getUserInfo办法            // dispatch(getUserInfo);            // dispatch 接管一个对象            dispatch(getUserInfo());        }    }}// 4.connect:关联Home组件与mapStateToProps和mapDispatchToProps办法export default connect(mapStateToProps, mapDispatchToProps)(About);

Redux-saga 补充:

takeEvery和takeLatest区别在于:是否可能残缺的执行监听办法
对于takeEvery而言, 每次拦挡到对应类型的action, 都会残缺的执行监听办法
对于takeLatest而言, 每次拦挡到对应类型的action, 都不能保障肯定可能残缺的 执行监听办法
store/saga.js

      • import {takeEvery, takeLatest, put, all} from 'redux-saga/effects'import {GET_USER_INFO, ADD_COUNT, SUB_COUNT} from './constants';import {changeAction} from './action';function *myHandler() {    // 获取网络数据    const data1 = yield fetch('http://127.0.0.1:7001/info')        .then((response)=>{            return response.json();        })        .catch((error)=>{            console.log(error);        });    const data2 = yield fetch('http://127.0.0.1:7001/info')        .then((response)=>{            return response.json();        })        .catch((error)=>{            console.log(error);        });    /*    如果咱们只须要保留一个数据, 那么间接通过 yield put 即可    然而如果咱们想同时保留多个数据, 那么咱们就必须借助另外一个函数:all()   * */     // yield put(changeAction(data));         yield all([     yield put(changeUserAction(data1)),     yield put(changeInfoAction(data2)),     yield put({type:'CHANGE_USER_NAME', name: data1.name}),     yield put({type:'CHANGE_USER_Age', name: data1.age}),         ])         console.log('执行到了监听办法的最初', data);     }     function *mySaga() {         /*         takeEvery和takeLatest区别: 是否可能残缺的执行监听办法         对于takeEvery而言, 每次拦挡到对应类型的action, 都会残缺的执行监听办法         对于takeLatest而言, 每次拦挡到对应类型的action, 都不能保障肯定可能残缺的执行监听办法         例如: 间断派发了3次GET_USER_INFO的action      那么对于takeEvery而言, myHandler就会被残缺的执行3次      那么对于takeLatest而言, 如果派发下一次同类型action的时候      上一次派发的action还没有解决完, 也就是上一次的监听办法还没有解决完      那么takeLatest会放弃还没有解决完的代码, 间接开始解决下一次的action        * */          // yield takeEvery(GET_USER_INFO, myHandler)              // yield takeLatest(GET_USER_INFO, myHandler)              /*              如果咱们只须要拦挡一个类型的action, 那么间接通过 yield takeEvery / yield takeLatest即可              然而如果咱们想同时拦挡多个类型的action, 那么咱们就必须借助另外一个函数: all()             * */               yield all([               yield takeEvery(GET_USER_INFO, myHandler),               yield takeEvery(ADD_COUNT, myHandler),               yield takeEvery(SUB_COUNT, myHandler),                   ]);               }
————————————————版权申明:本文为CSDN博主「奋斗吧,青年!」的原创文章,遵循CC 4.0 BY-SA版权协定,转载请附上原文出处链接及本申明。原文链接:https://blog.csdn.net/lilygg/article/details/118256153