原文链接: https://interview.poetries.top/principle-docs/react/09-react%E7%BB%93%E5%90%88redux%E5%AE%9E%E6%88%98.html

TODO为例分析,实际开发中并不是那么简单,下面的原型只是开发中的一个原型,这个简单的例子,有助于掌握数据处理传递的原则。

一、定义constants

这一步不是必须的

    /**
     * 常量统一保存,便于管理
     */
    export const ADD_TODO = 'ADD_TODO';
    export const TOGGLE_TODO = 'TOGGLE_TODO';
    export const SET_VISIBILITY = 'SET_VISIBILITY';
    
    //controll todo wheher show or hide
    export const SHOW_ALL = 'SHOW_ALL';
    export const SHOW_ACTIVE = 'SHOW_ACTIVE';
    export const SHOW_COMPLETED = 'SHOW_COMPLETED';

二、定义actionCreator

    /**
     * 定义action creator
     */
    
    import * as actionType from '../constant/index';
    
    let nextTodo = 0;
    
    export const addTodo = (text)=>({
      type:actionType.ADD_TODO,
      id:nextTodo++,
      text
    })
    
    export const toggleTodo = (id)=>({
      type:actionType.TOGGLE_TODO,
      id
    })
    
    export const setVisibilityFilter = (filter)=>{
      return {
        type:actionType.SET_VISIBILITY,
        filter
      }
    }

三、定义reducer

拆分reducer

  • SetVisibility.js
    /**
     * 处理TODO可见与不可见的reducer
     */
    import * as actionType from '../constant/index';
    
    // 初始状态是自己设置的 后面的状态会转化
    // 接收当前状态(设置默认的过滤SHOW_ALL,如设置某些选项卡的active一样),和action返回新的state
    export const visibilityFilter = (state='SHOW_ALL',action)=>{
      switch(action.type) {
        case actionType.SET_VISIBILITY:
          return action.filter;
        default:
          return state;
      }
    }
  • addTodo.js
    /**
     * 定义处理action的reducers
     */
    import * as actionType from '../constant/index';
    
    //传入当前的状态空数、action
    export const todos = (state = [],action)=>{
      switch(action.type){ // 匹配用户触发的actionType
        case actionType.ADD_TODO:
        // 合并上一次的状态和当前的状态 返回todos数组
          return [
            ...state,//把数组展开合并
            {
              id:action.id,
              text:action.text,
              completed:false//用户控制TODO是否处于点击完成的状态 默认false 没点击
            }
          ]
          // TODO列表来回切换 遍历add_todo返回的数组 通过completed来判断
        case actionType.TOGGLE_TODO:
          return state.map(todo=>(todo.id===action.id)?{...todo,completed:!todo.completed}:todo)
        default:
          return state;//匹配不到返回state
      }
    }

合并reducer

    /**
     * 合并reducers
     */
    
    import { combineReducers } from 'redux'
    import {todos} from './addTodo';
    import {visibilityFilter} from './SetVisibility';
    
    export default combineReducers({
      todos, //这些键其实就是被拆分的状态,后面在容器组件中需要通过connect链接
      visibilityFilter
    });

四、定义store

    import { applyMiddleware, createStore } from 'redux';
    import reducer from '../reducers/index';
    import logger from 'redux-logger';
    
    // 创建store 用来存储状态
    export const store = createStore(
      reducer,
      applyMiddleware(logger) //处理日志中间件
    )

五、结合react-redux

这里忽略展示组件,完成源码看文章结尾

  • 首先我们在container组件中处理好之前分解合并的那些reducer的键,然后在通过connect链接,传递给展示组件的属性使用

容器组件处理

react-todos/src/container/FilterLink.js

    // 处理数组过滤
    
    import { connect } from 'react-redux';
    import Link from '../components/Link';
    import { setVisibilityFilter } from '../actions/index';
    
    // 这里的ownProps指的是 FilterLink
    // 这里的state其实就是之前分解的todos,visibilityFilter
    const mapStateToProps = (state, ownProps) => {
      return {
        active:ownProps.filter === state.visibilityFilter
      }
    }
    
    /**
     如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。
     */
    const mapDispatchToProps = (dispatch, ownProps) => ({
    // 这里处理对应的事件,传递给展示组件的属性
      onClick: () => {
          dispatch(setVisibilityFilter(ownProps.filter))
      }
    })
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(Link);
  • 分析
    • 这里通过connect组件把之前reducer处理的那些状态链接
    export default combineReducers({
      todos, //这些键其实就是被拆分的状态,后面在容器组件中需要通过connect链接
      visibilityFilter
    });

react-todos/src/container/VisibilityTodoList.js

    /**
     * 处理可见于不可见组件的逻辑
     */
    import {connect} from 'react-redux';
    import TodoList from '../components/TodoList';
    import {toggleTodo} from '../actions/index';
    import * as actionType from '../constant/index';
    console.log(toggleTodo)
    
    // todos是返回的数组,filter是过滤的选项如SHOW_ALL SHOW_ACTIVE.
    const getVisibilityTodos = (todos,filter)=>{
      switch(filter) {
        case actionType.SHOW_ALL:
          return todos;
        case actionType.SHOW_ACTIVE:
          return todos.filter(t => !t.completed);
        case actionType.SHOW_COMPLETED:
          return todos.filter(t => t.completed);
        default:
          throw new Error('未知的'+filter);
      }
    }
    
    // 把状态转化为展示组件的属性转递过去
    /**
     *
     * @param {*} state 也就是
     * export default CombineReducers({
      Todos,
      SetVisibility
      *});
     * @param {*} ownProps 返回的容器组件本身的参数 如<Filter name="poetries">此时的ownProps就是name了
     */
    
    const mapStateToProps = (state) => {
      return {
        todos: getVisibilityTodos(state.todos,state.visibilityFilter),
        count:state.todos.length
      }
    }
    
    /**
     * 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出
     */
    const mapDispatchToProps = {
        onTodoClick: toggleTodo
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(TodoList)

react-todos/src/container/addTodos.js

    /**
     * Addtodo的处逻辑
     */
    import React, { Component } from 'react';
    import {addTodo} from '../actions/index';
    import AddTask from '../components/addTodo';
    import {connect} from 'react-redux';
    
    const mapStateToProps = (state, ownProps) => ({
      
    })
    
    const mapDispatchToProps = {
      addTodo:addTodo
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(AddTask);

到此分析完毕,展示组件就不分析了,展示组件本身是没有数据的,需要container处理传递

完整的源码

https://github.com/poetries/react-todos

阅读全文

Last Updated:
Contributors: guoli