「推荐」React 通信之 Redux,看这一篇就能掌握

    |     2020年4月19日   |   reactjs, web前端技术   |     0 条评论   |    1362

React 组件常在 componentDidMount 里初始化 Model 并监听 change,Model 变了再 setState 重绘;componentWillUnmount 时取消监听并销毁 Model。状态一复杂,单向数据流会越来越绕。Redux 就是为了把 state 收拢到单一仓库里。

redux1

存在即合理。下图左侧是 React 单向流动在复杂状态面前的麻烦;右侧是 Redux 维护的单一状态树。三大特性:唯一数据源、保持只读、只能用纯函数改数据。

redux20200419194631

概念 角色
Action 把数据从应用传到 store 的有效载荷
Store 保存数据的地方
Dispatcher view 发出 Action 的唯一办法
Reducer 根据 Action 算出新 state,view 才会变
State store 里存的数据

一、用借书把流程串起来

回想去图书馆借书,对照上图右侧的树:

  • 你去图书馆;
  • 询问管理员有没有《西部世界 iii》;
  • 管理员查阅库存;
  • 反馈给你:自己网上找资源去;
  • 你去网上找资源。

这就是 Redux 全流程。询问 + 查阅 = 反馈,也就是 action + state = new state。有果必有因。

20200419194702

20200419194725

20200419194743

二、UI 组件和容器组件

React-Redux 把组件分成 UI 组件(presentational)和 容器组件(container)。

UI 组件负责呈现,容器组件负责数据和逻辑。

两者都有怎么办?拆开:外面容器、里面 UI。容器对外通信,把数据传给 UI 渲染。规定是:UI 由用户提供,容器由 React-Redux 自动生成——视觉层归你,状态管理交给它。以上摘自阮一峰网络日志,建议初学者多读几遍。

  • Action: 把数据从应用传到 store 的有效载荷;
  • Store: 保存数据的地方;
  • Dispatcher: view 发出 Action 的唯一办法;
  • Reducer: Store 收到 Action 后必须给出新 state。Reducer 即根据 Action 计算 state;
  • State: store 里面存储的数据。

QQ╜╪═╝20200419195109

QQ╜╪═╝20200419194929

三、加数示例:走完一遍流程

一个简单加数,覆盖 Redux 各概念、UI 与容器的关系,以及 Provider。

import React, { Component } from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider, connect } from "react-redux";

// 生成组件
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props;
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    );
  }
}

Counter.propTypes = {
  value: PropTypes.number.isRequired,
  onIncreaseClick: PropTypes.func.isRequired
};

// 定义action
const increaseAction = { type: "increase" };

/**
 * Reducer函数
 * @param {*} state 默认值
 * @param {*} action 根据action来计算state.
 */
function counter(state = { count: 0 }, action) {
  const count = state.count;
  switch (action.type) {
    case "increase":
      return { count: count + 1 };
    default:
      return state;
  }
}

/**
 * 创建store -> 需要reducer -> 默认值+action
 */
const store = createStore(counter);

/**
 * 建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。
 * @param {*} state
 */
function mapStateToProps(state) {
  return {
    value: state.count
  };
}

/**
 * 建立 UI 组件的参数到store.dispatch方法的映射
 * 定义了哪些用户的操作应该当作 Action,传给 Store。
 * 它可以是一个函数,也可以是一个对象。
 *
 * 1. 函数:
 *      - 参数:
 *        可以拿到dispatch和ownProps(容器组件的props对象)两个参数;
 *      - 返回值
 *        一个对象,每个键值对都是一个映射,[键名对应函数名]
 *         定义了 UI 组件的参数怎样发出 Action。
 * 2. 对象
 *      它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,
 *      会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
 * @param {*} dispatch
 */
function mapDispatchToProps(dispatch) {
  return {
    // 同名函数 onIncreaseClick, dispatch 一个action
    onIncreaseClick: () => dispatch(increaseAction)
  };
}

/**
 * connect 自动生成容器组件,将UI组件[Counter]与[容器组件]即关联起来。
 * Counter: UI 组件
 * mapStateToProps: 输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
 * mapDispatchToProps: 用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
 */
const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);

// React-Redux 提供Provider组件,可以让容器组件拿到state。
// Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了
// 原理: React中的context属性,
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

结合下图理解:

QQ╜╪═╝20200419194948

图片来源:https://juejin.im/post/5acdbe8f51882548fe4a7af1

四、Provider 究竟做了什么

最外层用 react-redux 的 Provider。它本身就是 React 组件,原理是 context:最外层实现 getChildContext 返回对象,内部用 this.context 拿。简化实现:

class Provider extends React.Component {
  getChildContext() {
    return { store: this.props.store }    
  }

  render() {
    return this.props.children
  }
}

// usage
const store = createStore();
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
, document.querySelector('#root'));

整页逻辑:

  1. 创建 Store:createStore(),接受 reducers 和默认值;
  2. createStore 对每个 reducer dispatch 一个 action.type=@@redux/INIT 的 action,得到默认 state;
  3. render() 时,子组件用 this.context.store.getState() 拿 state,加上 ownProps 交给 mapStateToProps,构成 props;
  4. UI 触发操作,mapDispatchToProps 发出对应 action;
  5. store 收到后 reduce 出新 state,再调各子组件的映射生成 props(原文写 mapDispatchToProps,实际更新视图靠的是订阅后的 mapStateToProps);
  6. 最后 Provider 驱动需要更新的组件重渲染。

五、TodoList:自己走一遍

react-redux 的 TodoList demo 建议从头实现一遍。代码虽多,对照上面的理解就能过;不要眼高手低。

QQ╜╪═╝20200419195003

六、参考

一幅图明白React-Redux的原理
解析:让你弄懂redux原理
https://www.cnblogs.com/nayek/p/12369418.html
https://www.processon.com/view/5db55958e4b0e433944fdc87
https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

原文

一句话总结:Redux 用唯一只读 store 和纯函数 reducer 消化 action;React-Redux 再用容器组件和 Provider 的 context 把 state 灌进 UI。

转载请注明来源:「推荐」React 通信之 Redux,看这一篇就能掌握
本文链接地址:https://ai.zhousir.top/?p=2874
回复 取消