Flux 使用整理

版权声明:转载请注明原文地址 https://blog.csdn.net/mjzhang1993/article/details/82689014

Flux 使用整理

注意:代码中用到了 Flow | Immutable ,可能会影响阅读,请忽略相关代码

源码

说明

Flux 是 Facebook 开发,用来帮助 React 之类的应用做状态管理的模式,它利用单向数据流,来梳理复杂的状态

一个 Flux 应用由三个部分组成

  • dispatcher 负责分发事件
  • store 负责保存数据,并且响应事件更新数据
  • view 负责订阅 store 中的数据,并且渲染页面

基本流程

  1. 用户触发视图事件
  2. 事件中通过调用(直接或者间接) dispatcher 实例的 dispatch 方法触发一个 action 事件
  3. action 的触发会导致 store 中进行数据变更
  4. 监听 store 中数据的 view 获得更新

原则

  1. 数据在 Flux 应用中的流向是单一方向的 Action -> Dispatcher -> Store -> View
  2. Dispatcher 是一个中央集线器,管理所有Flux 应用中的数据流,它只是回调到 store 中的注册表,用来分配 action 到 store,当一个 action 被 dispatch 的时候,所有注册过这个 Dispatcher 的 store 都会收到通知,判断 action.type 之后触发相应的更新
  3. Store 中包含应用的状态和逻辑,类似于传统 MVC 架构中的 Model ,Store 将自己注册到 Dispatcher 并提供回调,在 Store 的注册回调中,一个基于action.type 的 switch 语句用于解释分析,并为 Store 内部的方法提供适当的钩子,钩子中对当前状态更改,更改后会触发广播事件,这个时候视图查询到状态更改,更新自己

flux/utils

flux/utils 是一个基础的,帮助实现 Flux 的基本实用程序类,提供一下几个主要的类

  1. Store
  2. ReduceStore
  3. Container

结合 flux/utils 的 react 项目最佳实践(代码结构划分)

  1. Stores
    • 可以借助 flux/utils 的 ReduceStore 实现用于缓存数据
    • 对外提供可访问数据的方法
    • 响应 dispatcher 分发的 action
    • 只有在 dispatch 可以触发 data 的更新
    • 按照页面分离,也就是每个页面创建一个 Store
  2. Actions
    • 有 action creater 创建,描述了触发 action 的内容
  3. Containers
    • 可以借助 flux/utils 提供的 Container.create() 方法辅助生成一个 react 容器组件
    • 主要的功能是从 store 中获取 data 并保存为 这个容器组件的 state
    • 容器组件不进行 UI 的定义
  4. Views
    • 普通的 react 组件,用来展示 UI
    • 所有需要用到的 store 中数据都通过容器组件传入 props

基本实现流程

1. 定义全局唯一的 Dispatcher 实例 /src/store/Dispatcher.js

Dispatcher 也可以定义多个,这样要触发哪个 Store 的更新,就找到注册 Store 时传入的那个 Dispatcher,来触发 dispatch(action)
设置多个 Store 共同绑定同一个 Dispatcher 时,要注意 Store 中定义的 action.type 不要重复

  // @flow

  /*
  * 这里将 Dispatcher 设置为全局只有一个,要确保不同的 Store 中响应的 action.type 不能重复
  * */

  // 创建全局唯一 Dispatcher

  import { Dispatcher } from 'flux';

  const instance: Dispatcher = new Dispatcher();

  // 这样方便直接引用 dispatch
  export const dispatch = instance.dispatch.bind(instance);

  export default instance;

2. 创建某个页面的 Store 数据 /src/store/{pageName}/store.js

  1. 创建一个继承 ReduceStore 的 pageStore 类, 最后输出他的实例, ReduceStore 是 ‘flux/utils’ 提供的
  2. pageStore 中有两个需要重写的方法 getInitialState | reduce
  3. 定义 getInitialState 方法返回一个初始化的 state
  4. 定义 reduce 方法接受旧的 state 和 action ,根据 action.type 触发不同的更改,返回新的 state
  5. 与 Redux 不同, Flux 官方建议在 store 中将 state 的处理逻辑提取出一个内部方法
// @flow

  /*
  * login 页面 store
  * */

  import {ReduceStore} from 'flux/utils';
  import {Map, fromJS} from 'immutable';
  import Dispatcher from '../Dispatcher';
  import {CHANGE_INPUT_DATA, INPUT_FEEDBACK} from './constants';

  type State = Map<string, any>

  const initialData = fromJS({
     username: {
        value: '',
     },
     password: {
        value: '',
     },
     loginSuccess: false
  });


  class loginStore extends ReduceStore<State> {
     state: State;

     // 初始化 state
     getInitialState(): State {
        return initialData.merge({})
     }

     /*
        action 的处理部分,每次 dispatch 都会他通知到这个函数,然后进行 action.type 的对比
     */ 
     reduce(state: State, action: Object): State {
        switch (action.type) {
           case CHANGE_INPUT_DATA:
              return state.mergeDeep(action.payload);
           case INPUT_FEEDBACK: 
              return this._inputFeedBack(state, action);
           default: return state;
        }
     }

     // 将 state 的更新逻辑提取出一个方法
     _inputFeedBack(state: State, action: Object) {
       const success = action.payload.success;

       if (success) {
         const newState = state.merge(initialData);

         return newState.set('loginSuccess', success);
       }

       return state.set('loginSuccess', success);
     }
  }

  // 输出的是一个 Store 的实例,这个实例中绑定了指定的 Dispatcher
  export default new loginStore(Dispatcher);

3. 创建 对应这个页面的 actions-creator 文件 /src/store/{pageName}/actions.js

  1. 在这个文件中引入 dispatch, dispatch 是 Dispatcher 的方法,用来分发 action
  2. 定义一个 action creator 函数,这个函数中可以通过 dispatch 一个 含有 type 属性的对象来分发 action
  3. 页面用到哪个 Store 就使用 Store 对应的 Dispatcher
  import {dispatch} from '../Dispatcher';

  export function changeInputData(newInputData: InputData) {
     return dispatch({
        type: CHANGE_INPUT_DATA,
        payload: newInputData
     })
  }

4. 提取 action.type 为常量

由于 action.type 在 action 中和 store 中都会用到,一次需要将其提取出为常量,防止拼写错误引起 BUG 提取到 /src/store/{pageName}/constants.js

5. 组件中使用
  1. 定义一个容器组件 Page 在 /src/container/{pageName}.js
  2. 这个组件被 Container.create(Page) 高阶组件包裹后,返回新的组件 Container 是 ‘flux/utils’ 提供的高阶组件,用于将 flux 的 store 与组建立联系
  3. 在我们自定义的组件 Page 中要定义两个固定的静态方法 getStore | calculateState,(高阶组件中会用到
  4. getStore 用来将我们创建的 store 与组件建立联系,他需要返回一个包含多个 store 的数组
  5. calculateState 会返回一个对象,对象中的属性值对应了 从 store 中获取的 状态,这属性值会作为 组件的 this.state 的属性值使用
  6. 我们之前定义的 actions 可以在这里引入
  7. 最后 引入的 action 与 state 又可以传给子组件作为 props 使用

    // @flow
    
    /*
    + Login 页面 使用 antd Form
    + */
    
    import * as React from 'react';
    import {Container} from 'flux/utils'
    import LoginCom from '../components/Login/index';
    import {changeInputData, postDataToLogin} from '../store/login/actions';
    import loginStore from '../store/login/store';
    
    type State = {
      loginState: Map<string, any>
    }
    
    class Login extends React.Component<any, State> {
    
      static getStores() {
        return [loginStore]
      }
      static calculateState(prevState: State): State {
        return {
          loginState: loginStore.getState()
        }
      }
    
      render(): React.Element<any> {
        return (
          <LoginCom
            loginState={this.state.loginState}
            changeInputData={changeInputData}
            postDataToLogin={postDataToLogin}
          />
        )
      }
    }
    
    export default Container.create(Login);

猜你喜欢

转载自blog.csdn.net/mjzhang1993/article/details/82689014