Redux

作者: 刘三慢 | 来源:发表于2019-03-26 11:40 被阅读0次

    单项数据类框架始祖Flux

    Flux的目的是用来替代Backbone.js、Ember.js等MVC框架。

    MVC框架将应用分为三个部分:
    Model(模型) 负责管理数据,大部分业务逻辑放在Model中;
    View(视图) 负责渲染用户界面,避免在View中涉及业务逻辑;
    Controller(控制器) 负责接受用户的输入,根据用户的输入调用对应的Model部分的逻辑,把产生的数据交给View部分,让View渲染出必要的输出;
    MVC框架的缺点
    MVC框架提出的数据流很理想,但是在实际框架视线中,总是允许View和Model直接通信,从而出现以下情况。

    MVC框架的缺点
    在MVC框架中,系统能提供什么的服务,通过Controller 暴漏函数来实现。每增加一个功能,Controller就要增加一个函数。

    Flux包含四个部分:
    Dispatcher 处理动作分发,维持Store之间的依赖关系;
    Store 负责存储数据和处理数据逻辑;
    Action 驱动 Dispatcher 的 JavaScript 对象;
    View 视图部分,负责显示用户界面;
    在Flux中 Dispatcher 始终只需要暴漏一个函数Dispatch,当需要增加新的功能时,只用增加一种新的Action类型,Dispatcher 对外接口不改变。

    Redux的基本原则

    Flux的基本原则是“单向数据流”,Redux在此强调三个基本原则
    1.唯一数据源
    应用的状态应该只存储在唯一一个Store上,这个唯一Store上的状态,是一个树形的对象,每个组件往往只是树形象上的一部分数据,如何设计Store上状态的结构,就死Redux应用的核心问题。
    2.保持状态只读
    不能直接修改状态,要修改Store的状态,必须通过派发 action 对象完成。
    3.数据改变只能通过纯函数完成
    纯函数就是Reducer,reducer接受两个参数,第一个参数state是当前的状态,第二个参数action是接受到的action对象。

    Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说会通过store.dispatch() 将 action 传到 store。
    约定:action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。除了 type 字段外,action 对象的结构完全由你自己决定。
    Action 创建函数
    Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。在 Redux 中的 action 创建函数只是简单的返回一个 action:

    function add(text) {
      return {
        type: TYPE_NAME,
        text
      }
    }
    

    Redux 中只需把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程

    dispatch(addTodo(text))
    

    Reducers 指定了应用状态的变化如何响应actions并发送到 store 的,actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。

    (previousState, action) => newState
    

    reducer规范:(不能做)
    修改传入参数;
    执行有副作用的操作,如 API 请求和路由跳转;
    调用非纯函数,如 Date.now() 或 Math.random()。

    Store 就是把action、reducer联系到一起的对象。Store 有以下职责:

    • 维持应用的 state;
    • 提供 getState() 方法获取 state;
    • 提供 dispatch(action) 方法更新 state;
    • 通过 subscribe(listener) 注册监听器;
    • 通过 subscribe(listener) 返回的函数注销监听器。

    注意 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合而不是创建多个 store。
    详细使用方法请看Redux中文文档

    容器组件和展示组件

    承担第一个任务的组件,也就是负责和 Redux Store 打交道的组件,处于外层,被称为容器组件;对于承担第二个任务的组件,也就是专心负责渲染界面的组件,处于内层,叫做展示组件

    组件分工

    展示组件是一个纯函数,根据 props 产生结果。

    组件Context

    在一个应用中,不同组件都会调用全局的Store,在组件中直接导入Store不利于组件复用,应该只在最顶层调用一次Store,将Store从上层组件传递给子组件,如果用props传递,所有的组件props都要增加对这个支持,很麻烦。React提供了一个Content的功能,能解决这个问题。
    Context(上下文环境),让一个树状组件上所有组件都能访问一个共同的对象,需要上级组件和下级组件配合。

    //定义在src文件夹下,是一个通用的 context 提供者
    import React, {Component} from 'react'
    import PropTypes from 'prop-types'
    
    class Provider extends Component {
      getChildContext() {
        //返回的代表Context的对象
        return {
          store: this.props.store
        };
      }
    
      render() {
        //简单的把子组件渲染出来代表Provider标签之间的组件
        return this.props.children;
      }
    }
    //为了Provide比较通用,所以store从外部传递进来
    //为了让Provider能够被React认可为一个Context提供者,需要指定Provider的childContextTypes
    Provider.childContextTypes = {
      store: PropTypes.object
    };
    
    
    export default Provider;
    

    每个React组件都有一个特殊属性 children,代表的是子组件。

    子组件中对context的使用

    className.contextTypes = {
      store: PropTypes.object
    }
    

    在子组件中对store的访问通过 this.context.store 完成。由于自定义了构造函数,所以构造函数要添加 context 参数

     constructor(props, context) {
       super(props, context);
       //es6 super(...arguments) 
    }
    

    context的使用:必须谨慎使用,只有对那些每个组件都可能使用,但是中间组件又不可能使用的对象才必须使用Context。

    具体使用方法请参考聊一聊我对 React Context 的理解以及应用-张国钰

    后记 将组件拆分为容器组件和展示组件,以及利用 React 的 Context 提供一个所有组件都可以直接访问的 Context,这两种方法都有自己的套路来改进 React 应用,可以将这两种方法的套路部分提取出来,已经有一个库实现,就是 react-redux

    相关文章

      网友评论

          本文标题:Redux

          本文链接:https://www.haomeiwen.com/subject/vfayvqtx.html