yayxs / front-end-video-tutorial

前端知识视频化 / 视频分享 / 视频教程
11 stars 3 forks source link

【状态管理】react中redux、react-redux实操指南 #11

Open yayxs opened 4 years ago

yayxs commented 4 years ago

一、Redux

1. readux的概述

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

2 . 三大原则

3 . 组成部分

Action

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

在实际的开发中我们习惯上是action创建函数

const addAction = (params)=>{
    return {
        type:ADD,
        ...params
    }
}

Reducer

现在我们依旧不说store 这个概念,现在动作有了,但是action它只是描述了一下这个动作,但并不知道咋更新数据,提到数据,我们假使

{
    num:0
}

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

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

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;
};

注意

Store

获取状态

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 这个库看起来是没有问题,但是

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

1. 什么是react-redux

Provider 组件

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. 使用

 <>
      <Provider store = {store}>
        <List></List>
        <Detail></Detail>
      </Provider>
    </>

combineReducers

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;

创建组件

三、Redux-Saga

不管怎么说,如上提及数据流操作只支持同步的操作,实现异步的话就需要中间件

1. 中间件

2 . 概述

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));

执行

sagaMiddleware.run(defSage);

4. 案例

saga 的辅助函数

 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;
    }
  };
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 用法可以参考官方文档

业务流程

获取数据

生命周期

四、Redux-Thunk

五、redux 原理

六、思考

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