• redux


    Redux介绍

     

    单一数据源

    整个单页应用的 state 都被储存在store的内部,可以通过store.getState()获取,再作为props传给对应的组件。

    state应该尽量少嵌套扁平化,通过id相互引用数据。

    import { createStore } from 'redux';
    const store = createStore(reducer);           //stroe需要reducer计算新的state
    
    //createStore函数的简单实现
    const createStore = (reducer) => {
      let state;
      let listeners = [];
      //返回store中的state tree的API
      const getState = () => state;
      //store.dispatch()会自动调用reducer生成新的state,并执行注册的回调函数
      const dispatch = (action) => {   
        //调用reducer更新state
        state = reducer(state, action);
        listeners.forEach(listener => listener());
      };
    
      //注册回调函数,通常是render或setState
      const subscribe = (listener) => {
        listeners.push(listener);
    return () => {
        //注销回调函数
          listeners = listeners.filter(l => l !== listener);
        }
      };
    
      dispatch({});
      return { getState, dispatch, subscribe };
    }; 

    State 为只读

    为防止出现race condition和deep equal的遍历,state应为只读,不能直接修改state,每个state都是新对象,而改变 state 的方法只有dispatch action。

    使用纯函数reducer来修改state

    reducer 是纯函数,接收prestate 和 action,返回新的 state。reducer必须保证同样的参数输入其输出一样,所以redux规定在Reducer函数里面不能直接改变 State,必须返回新的对象。在reducer中,不能改写参数,不能调用系统 I/O 的API,不能调用Date.now()或者Math.random()等不纯的方法,只是单纯的执行计算。例如 API 调用或路由跳转等有副作用的操作应该在 dispatch action 前发生。

    function reducer(state, action) {
       switch (type) {
        case ADD_CHAT:
                //若state 是一个对象,不能直接修改state,而应使用如下方式返回新建的state
          return Object.assign({}, state, { newState });   
              // 或者return  { ...state, ...newState };
        default: return state;      //如果action不属于该reducer负责,需要返回原state
      }
    }
    
    // 若State 是一个数组
    function reducer(state, action) {
      return [...state, newItem];
       //或者
       return […state].concat(newItem);
    }        

    也可以使用immutable.js把 State 对象设成只读,这样改变State时只能通过生成一个新对象。

    因为reducer函数的作用只是接收一个state计算后返回一个新的state,所以在应用中可以将多个分布在不同目录的reducer(函数)合成一个rootReducer,每个小的reducer只负责管理全局store中对应的一部分state:

    export default function todoAppReducer(state = {}, action) {
      return {
        //只负责store中visibilityFilter和todos字段的state,别的state不可访问
        visibilityFilter: visibilityFilter(state.visibilityFilter, action),
        todos: todosReducer(state.todos, action)                  //state.todos是大state中todosReducer负责的state
      }
    }

    //等价于

    import { combineReducers } from 'redux';
    const todoAppReducer = combineReducers({
      visibilityFilter,                //store的state树中的节点名和reducer函数名相同
      todos:todosReducer             //      设置不同的对应名字
    })
    export default todoApp;
    //combineReducer将子reducer合成一个大reducer。每个 reducer 根据 key 来筛选出 state 中的一部分数据并处理。最后这个生成的函数再将所有 reducer 的结果合并成一个大的对象,从而更新store。
    const combineReducers = reducers => {
      return (state = {}, action) => {
        return Object.keys(reducers).reduce(
          (nextState, key) => {
            nextState[key] = reducers[key](state[key], action);
            return nextState;
          },
          {}
        );
      };
    };

      array.reduce(function(accumulator, currentValue, currentIndex, array), initialValue)

      注意:若无initialValue,reduce 会跳过索引0从索引1开始执行 callback,若提供 initialValue,则从索引0开始。

    如果需要在一个 Reducer 中访问另外一个 Reducer 负责的 state,这时需要自己写root Reducer或用reduce-reducers来控制。

    state可以嵌套,对应的reducer亦可以嵌套,最终由combineReducer方法生成finalReducers,对应的state结构和reducer的嵌套结构相同。

     

    action 是一个用于抽象描述已发生事件的普通对象,可以被序列化,是 store 数据的唯一来源。应尽量减少在 action 中传递的数据。

    action Creator

    View要发送多少种消息,就会有多少种 Action。可以定义一个Action Creato函数来生成 Action,不用每次都写样板代码。

    const ADD_TODO = '添加 TODO';
    function addTodo(text) {
      return {
        type: ADD_TODO,
        text
      }
    }
    store.dispatch( addTodo('Learn Redux') );      //store会自动调用reducer得到新的state
    store.dispatch( addTodo('Learn React) ); 

    可以直接通过 store.dispatch() 调用 dispatch() 方法,也可以用 react-redux 包提供的 connect()方法调用,更方便。

    @connect() es7的类修饰器

    React-Redux

    Redux 和 React 之间没有直接关系,可以在react中直接使用store的方法,也可以通过react-redux模块进行绑定。

     

    容器组件

    展示组件

    Location

    最顶层,路由处理

    中间和子组件,可复用

    Aware of Redux

    读取数据

    从 Redux 获取 state

    从 props 获取数据

    修改数据

    向 Redux 派发 actions

    从 props 调用回调函数

     

    容器组件Container Components:使用redux的API进行数据管理,带有内部状态。

    展示组件Presentational Components:不使用任何 Redux 的 API,通过props传递数据,负责UI呈现,没有状态(不使用this.state这个变量)。

    connect方法

    从 UI 组件生成容器组件(UI component => container container),将react (ui)和redux (store)绑定起来,可以在嵌套的不同层次使用。

    /*container component
    *容器组件负责管理数据和业务逻辑,UI组件只显示视图
    */
    import { connect } from 'react-redux'
    import { setVisibilityFilter } from '../actions'
    import Link from '../components/Link'
    
    //mapStateToProps将state映射到UI组件的参数(props),返回一个对象,其属性代表 UI 组件的同名参数。
    //mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,最后触发 UI 组件的重新渲染。如果不传入mapStateToProps参数,store的更新不会引起UI组件的更新。
    const mapStateToProps = (state, ownProps) =>{
      //state指reduce负责的那部分state,ownProps指容器父组件传入的所有属性
      //active是传给子组件的prop属性,父组件传入的其他props也会在子组件中
      return{
        active: ownProps.filter === state.visibilityFilter
      }
    }
    
    //如果mapDispatchToProps为函数,则应返回一个对象,其每个键值对都是一个映射,定义了用户对 UI 组件的操作怎样分发出 Action。
    //若mapDispatchToProps是一个对象,则属性名对应UI组件的同名参数,属性值是一个当作Action creator的函数,其返回的action会由Redux自动发出给store。
    const mapDispatchToProps = (dispatch, ownProps) => ({
        //onClick是传给子组件调用的方法,在view触发action时调用
      onClick: () => {
        dispatch(setVisibilityFilter(ownProps.filter))
      }
    })
    
    //如果不传入mapStateToProps/mapStateToProps,将默认只传入dispatch和父组件传入的props,不传入store
    const FilterLink = connect(     // FilterLink是容器组件
      mapStateToProps,          // 将全局state对象映射到 UI 组件
      mapDispatchToProps        // UI 组件将传出action对象的方法
    )(Link)             //Link为UI组件
    
    export default FilterLink
    /*UI 组件,只负责UI的呈现*/
    import React from 'react'
    
    //active和onClick是父容器组件通过connect方法传入的props属性
    const Link = ({ active, children, onClick }) => {
      if (active) {
        return <span>{children}</span>
      }
      return (
        <a href="#"
           onClick={e => {
             e.preventDefault()
             onClick()
           }}
        >
          {children}
        </a>
      )
    }
    
    export default Link

    <Provider> 组件

    使用Provider组件包裹根组件,将store传入容器组件,并通过组件的context属性使嵌套的所有子组件都能获得根state而不用一级一级传递下去。

    //在任意组件引用全局store,尽量不要直接操作store的实例
    Link.contextTypes = {
      store: React.PropTypes.object
    }
  • 相关阅读:
    JSON的序列化和反序列化
    XML的序列化和反序列化
    FTP相关操作
    滑动换屏——Fragment
    AngularJS中复选框(checkbox)的ng-model
    AngularJS将时间戳格式化处理
    Let definitions are not supported by current JavaScript version
    MyBatis中的 suffixOverrides="," 失效
    MyBatisSystemException: Malformed OGNL expression: status != null
    MySQL字符串截取
  • 原文地址:https://www.cnblogs.com/kevin2chen/p/7411838.html
Copyright © 2020-2023  润新知