【面试官】 你必定用过React中的Redux,来讲说吧

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

1、Redux

1. redux的概述

通用的状态管理辅助工具,习惯上咱们能够结合ReactJs 来使用,在组件化开发过程当中,组件的数据状态不得不集中化管理,这也是咱们使用Redux的缘由之一,是一个数据的容器。习惯上咱们称之为js库ios

2 . 三大原则

  • 单一数据源,惟一的状态仓库
  • state是只读 (派发action)
  • 纯函数执行修改数据的修改 (编写 reducers)

3 . 组成部分

  • state
    • 服务端的数据
    • UI数据
    • app state
  • Action
  • Reducer
  • Store

Action

action顾名思义动做行动 行为,一言以蔽之,它是把数据从应用传到仓库的一个动做,也就是这个数据仓库git

  • JS对象es6

  • 格式github

    {
        type:ADD, // type 字段是约定,你们的约定,表示动做的名字,
        index:1,
        content:'这是一点内容'
    }
    复制代码
    • 对象的type字段表示执行的动做;字符串常量
    • index 惟一的ID,主要是一个标识
    • content(或者其余)JS对象什么字段均可以啊,一个字段而已

在实际的开发中咱们习惯上是action建立函数typescript

const addAction = (params)=>{
    return {
        type:ADD,
        ...params
    }
}
复制代码

Reducer

如今咱们依旧不说store 这个概念,如今动做有了,可是action它只是描述了一下这个动做,但并不知道咋更新数据,提到数据,咱们假使redux

{
    num:0
}
复制代码

这个简单的js对象就是数据axios

ACTION是个普通的对象;REDUCER是个普通的函数api

  • 说普通也不普通,js的函数而已app

    • function(a,b){
          console.log(a+b)
      }
      复制代码
    • 可是没那么简单

  • 干净简单,

  • // 默认的数据
    const initData = {
        num:123
    }
    // reducer
    const counterReducer =(state=initData,action)=>{
        
      // 啥也不干,返回传进来的state(此时默认的initData)
       return state
    }
    
    复制代码
  • 怎么可能啥也不干呢

import { addAction } from "./actions";
// 默认的数据
const initData = {
  num: 123
};
// reducer
const counterReducer = (state = initData, action) => {
  // 判断传入的动做是什么类型
  switch (action.type) {
    case addAction:
      return Object.assign({}, state, {
        ...
      });
    default:
      return state;
  }

  // 啥也不干,返回传进来的state(此时默认的initData)
  // return state;
};


复制代码

注意

  • 不能修改传进来的数据
  • 在默认状况下,必定得返回旧的state

Store

  • 这就是那个状态仓库,维持状态
  • getState() 方法获取state
  • 提供 dispatch ()方法发送action
  • 经过subscribe()来注册监听

获取状态

getState()
复制代码

更新状态

dispatch(action) 
复制代码

也就是咱们说的派发一个动做

注册监听(订阅)

subscribe(listener)
复制代码

4 . 简单案例

在这个时候,有个问题,前边说的这一切,那咱们该怎么来建立这个仓库呢

yarn add redux
复制代码

这个库里就有方法,也就是咱们常说的redux

构建action

import { ADD_TYPE } from './actionTypes'
const addAction = (params)=>{
    return {
        type:ADD_TYPE,
        ...params
    }
}

export {
    addAction
}
复制代码

构建reducer

import { addAction } from "./actions";
// 默认的数据

// reducer
const counterReducer = (state = {num:123}, action) => {
  // 判断传入的动做是什么类型
  switch (action.type) {
    case addAction:
      return Object.assign({}, state, action);
    default:
      return state;
  }

  // 啥也不干,返回传进来的state(此时默认的initData)
  // return state;
};

export {
    counterReducer
}
复制代码

建立store

引入文件

import { createStore } from "redux";
import { counterReducer } from "./reducers";
复制代码

createStore

const store = createStore(counterReducer);
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 = ()=>{
    ReactDom.render( <App/>, document.querySelector('#root') ) }
// 上来的时候先渲染一次
render() 
// 订阅变动,每当数据发生的变化的时候,就从新渲染
store.subscribe(render)
复制代码

小结

经过一个简单的案例,咱们知道一个简易的流程:

  1. 首先构建一个action 返回一个对象必须有type属性
  2. 构建reducer 响应action t经过return 把数据传回store
  3. 利用redux这个库来建立一个store 传递写好的reducer
  4. 利用的$store.subscribe() 注册监听
  5. 能够经过store.getState() 取值

二 、React-Redux

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

  • 首先要导入store
  • 而后注册监听
  • 而后组件销毁的时候,咱们取消监听

这一波流的操做在每一个组件都要走一遍,显然是十分繁琐和重复的,这就须要看谁能不能帮帮我,这就是react-redux 若是须要把redux整合到react 中来使用就须要react-redux

1. 什么是react-redux

  • redux 官方出品

  • 可以更好的结合react 来管理数据

Provider 组件

  • 包裹在最外层的组件以外,这样可使全部的子组件均可以拿到state
  • 接收store 做为props 经过context 传递

connect 方法

  • 组件内部获取store 中的state
  • 经过connect 增强

mapStateToProps(state,ownProps)

const mapStateToProps = (state, ownProps) => {
    console.log(state)
    return state
    // return {
    // prop: state.prop
    // }
}
复制代码

mapDispathToProps(dispath,ownProps)

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    sendAction: () => {
      dispatch({
        type: "ADD_TYPE",
      });
    },
  };
};
复制代码

2. 使用

  • 安装相关的依赖

  • 构建store 和readucer

  • Provider组件实现

<>
      <Provider store = {store}>
        <List></List>
        <Detail></Detail>
      </Provider>
    </>
复制代码
  • connect

combineReducers

  • 函数,接收一个参数
  • 拆分reducer
import { createStore, combineReducers } from "redux";
// import { counterReducer } from "./reducers";
// import rootReducer from './reducers/index'

import { infoReducer } from "./reducers/infoReducer";
import { listReducer } from "./reducers/listReducer";

const reducer = combineReducers({
  infoReducer,
  listReducer,
});

// 构建store
const store = createStore(reducer);
export default store;

复制代码

建立组件

  • ComA A组件

    import React, { Component } from "react";
    import { connect } from "react-redux";
    class ComA extends Component {
      handleClick = () => {
        this.props.getInfo();
      };
    
      render() {
        return (
          <div>
            {/* <h3>{this.props.}</h3> */}
            <button onClick={this.handleClick}>获取信息</button>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state, ownProps) => {
      console.log(state.infoReducer);
      // return {
      //   prop: state.prop,
      // };
      // return state
      return {
        ...state.infoReducer,
      };
    };
    const mapDispatchToProps = (dispatch, ownProps) => {
      return {
        getInfo: () => {
          const actionCreator = {
            type: "GET_INFO",
          };
    
          dispatch(actionCreator);
        },
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps)(ComA);
    
    复制代码
  • ComB

    import React, { Component } from "react";
    import { connect } from "react-redux";
    
    class ComB extends Component {
      handleClick = () => {
        this.props.getList();
      };
      render() {
        return (
          <div>
            <button onClick={this.handleClick}>获取列表</button>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state, ownProps) => {
      console.log(state.listReducer)
      // return state
      return {
        ...state.listReducer
      }
    };
    
    const mapDispatchToProps = (dispatch, ownProps) => {
      return {
        getList: () => {
          const actionCreator = {
            type: "GET_LIST",
          };
          dispatch(actionCreator);
        },
      };
    };
    
    
    export default connect(mapStateToProps, mapDispatchToProps)(ComB);
    
    复制代码

  • infoReducer.js

    const info = {
      name: "yayxs",
    };
    
    const infoReducer = (state = {}, action) => {
      switch (action.type) {
        case "GET_INFO":
          return {
            ...info,
          };
    
        default:
          return state;
      }
    };
    
    export {
      infoReducer
    }
    复制代码
  • listReducer

    const listArr = [
      {
        id: 1,
        con: "耳机",
      },
    ];
    
    const listReducer = (state = {}, action) => {
      switch (action.type) {
        case "GET_LIST":
          return {
            listArr: [...listArr],
          };
    
        default:
          return state;
      }
    };
    
    export {
      listReducer
    }
    复制代码

3、Redux-Saga

无论怎么说,如上说起数据流操做只支持同步的操做,实现异步的话就须要中间件

1. 中间件

  • 自己就是一个函数
  • 应用在action 发布出去以后

2 . 概述

  • 用来管理反作用,其中包括像异步操做 ,让反作用的执行更加简单
  • es6的语法,参考阮老师

3. createSagaMiddleware

其中源码是这样的

export default function createSagaMiddleware<C extends object>(options?: SagaMiddlewareOptions<C>): SagaMiddleware<C> export interface SagaMiddlewareOptions<C extends object = {}> {
  /** * Initial value of the saga's context. */
  context?: C
  /** * If a Saga Monitor 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 sending 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?: EffectMiddleware[]
}
复制代码

导入

import createSagaMiddleware from "redux-saga";
复制代码

构建store

const store = createStore(sagaReducer, {}, applyMiddleware(sagaMiddleware));
复制代码
  • 第一个参数是reducer
  • 第二个initState
  • 第三个参数:中间件

执行

sagaMiddleware.run(defSage);
复制代码

4. 案例

saga 的辅助函数

  • takeEvery

  • takeLatest

  • throttle

  • SagaCom

handleClick = (type) => {
   
    switch (type) {
      case "takeEvery":
        this.props.dispatch({
          type: "takeEvery",
        });
        break;
      case "takeLatest":
        this.props.dispatch({
          type: "takeLatest",
        });
        break;

      case "throttle":
        this.props.dispatch({
          type: "throttle",
        });
        break;

      default:
        break;
    }
  };
复制代码
  • sages/index.js
import {
  takeEvery,
  takeLatest,
  throttle,
  select,
  call,
} from "redux-saga/effects";

import axios from "axios";
export function* defSage() {
  yield takeEvery("takeEvery", function* () {
    const state = yield select((state) => state.payload);

    const res = yield call(
      axios.post,
      `http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
      {
        ...state,
      }
    );

    console.log(res);
  });
  // 最后的一次,取消正在运行中
  yield takeLatest("takeLatest", function* () {
    const state = yield select((state) => state.payload);

    const res = yield call(
      axios.post,
      `http://rap2.taobao.org:38080/app/mock/249413/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:38080/app/mock/249413/mock-api/v1/users/login`,
      {
        ...state,
      }
    );

    console.log(res);
  });
}

复制代码

effect 建立器

详细的api 用法能够参考官方文档

  • select
  • call
  • take
  • put

业务流程

获取数据

  • 当页面一加载,而后发送一个获取数据的action
  • reducer 匹配对应的action 若是是一部的action 直接把数据返回
  • 在saga 里使用 takeEvery 来进行监听
  • call方法调用异步请求,传入请求的参数
  • put反作用发送action 成功或者是失败
  • 在reducer 里处理action

生命周期

  • componentDidMount获取数据
  • componentWillUpdate 处理数据

4、思考

  1. Hooks API ,也就是函数式的组件怎么监听页面数据的变化 ,而后执行刷新?
  2. redux-saga 中的辅助函数 takeEvery takeLatest throttle 在底层有什么区别?

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

相关文章
相关标签/搜索