之前写了一篇文章是通过flux实现的todos案例:

当时主要是为了理解flux的思想,不过貌似实际开发中使用redux+react-redux的更多一些,也进行入坑。

本质上redux也是flux的思想,至少我入坑的时候,没觉得他们的思想有什么不同,只不过暴露的api和实现方式不同。

当然react-redux对于storeprops的注入还是非常实用的,相比于引入一堆store/state,注入来使用更加方便。

所以用redux实现了todos,不过没有那么多功能,就是简单的增加完成显示

个人觉得,redux没有dispatcher这个玩意儿,直接通过reducer来进行action的触发,并且通过subscribe也不需要自己来搞事件监听响应,方便的太多.

一、介绍

1、项目构建:

项目构建使用的是自己搞得webpack3-react16-development的开发环境:

2、项目结构:

结构使用的是redux建议的组织模式,通过container进行storeprops传递。

只说明 src中的目录结构

|- src
    |- action
        |- TodoAction.js            // action
    |- components
        |- todo
            |- TodoFooter.jsx        // 底部组件
            |- TodoHeader.jsx        // 顶部组件
            |- TodoItem.jsx          // 单个Todo组件  
            |- TodoList.jsx          // Todo 列表组件
        |- Hello.jsx                 // 项目自带hello组件
    |- constant    
        |- TodoConstant.js           // 常量 
    |- container
        |- TodoApp.jsx               // Todo容器组件 
    |- images
    |- reducer
        |- TodoReducer.js            // reducer
    |- App.jsx                       // App组件
    |- index.html                    // 自带html模板文件
    |- main.js                       // 入口文件

二、效果

1.png

2.png

三、实现

1、TodoConstant.js 存储所用常量

之前使用flux写的todos示例,在Footer组件中引入了TodoConstant,所以本质上没有完全的从TodoApp分发。

而这个示例中,TodoConstant也是从TodoApp上进行分发的。

const TodoConstant = {
  ADD_TODO: 'ADD_TODO',
  TOGGLE_COMPLETE: 'TOGGLE_COMPLETE',
  SHOW_FILTER: 'SHOW_FILTER',
  FILTER: {
    SHOW_ALL: 'SHOW_ALL',
    SHOW_ACTIVE: 'SHOW_ACTIVE',
    SHOW_COMPLETED: "SHOW_COMPLETED"
  }
};

export default TodoConstant;

2、TodoAction.js 定义Action

不同的一点是,我在reducer中直接创建并且返回了store.

当然,redux的示例中,是在container中创建的store,然后使用。不过个人认为,在组件中,创建store并不是特别好。组件就是组件,即使container,那也是组件。

import TodoConstant from '../constant/TodoConstant';

const TodoAction = {
  // 增加 todo
  addTodo(text) {
    return {
      type: TodoConstant.ADD_TODO,
      text
    }
  },
  // 完成或者撤销完成状态
  toggleComplete(index) {
    return {
      type: TodoConstant.TOGGLE_COMPLETE,
      index
    }
  },
  // 更改显示的选择样式
  showFilter(filter) {
    return {
      type: TodoConstant.SHOW_FILTER,
      filter
    }
  }
};

export default TodoAction;

3、TodoReducer.js 定义Reducer

import TodoConstant from '../constant/TodoConstant';

import { createStore, combineReducers } from 'redux';

// 初始化的 list
const list = [
  { text: 'AAAAAA', completed: false },
  { text: 'CCCCCC', completed: true },
  { text: 'DDDDDD', completed: false },
];
// 定义 todos的reducer
// 两部分操作 一部分处理ADD_TODO 另一部分处理 TOGGLE_COMPLETE
const todos = (todos = list, active) => {
  switch (active.type) {
    case TodoConstant.ADD_TODO:
      return [
        ...todos,
        {
          text: active.text,
          completed: false
        }
      ];
    case TodoConstant.TOGGLE_COMPLETE:
      return todos.map((item, index) => {
        if (index === active.index) {
          return Object.assign({}, item,item.completed = !item.completed);
        } else {
          return item;
        }
      });
    default:
      return todos;
  }
}
// 处理 filter 
const filter = (filter = TodoConstant.FILTER.SHOW_ALL, active) => {
  switch (active.type) {
    case TodoConstant.SHOW_FILTER:
      return active.filter;
    default:
      return filter;
  }
}
// 两个reducer进行组合
const reducer = combineReducers({
  todos: todos,
  filter: filter
});

const store = createStore(reducer);

export default store;

3、 TodoApp.jsx

容器组件,单独说容器组件主要是因为容器负责了方法和state的向下分发,因此基本上逻辑都是集中在这一块的。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import TodoConstant from '../constant/TodoConstant';
import TodoAction from '../action/TodoAction';

import TodoHeader from '../components/todo/TodoHeader';
import TodoList from '../components/todo/TodoList';
import TodoFooter from '../components/todo/TodoFooter';

class TodoApp extends Component {
  constructor(props) {
    super(props);
  }
  // 实际上处理增加todo的方法 这个方法通过props分发到了Todoheader
  addTodoHandle(text) {
    this.props.dispatch(TodoAction.addTodo(text));
  }
  // 处理完成与取消完成状态的todo 这个方法通过props分发到了TodoList再分发到了TodoItem
  toggleComplete(index) {
    this.props.dispatch(TodoAction.toggleComplete(index));
  }
  // 处理显示 通过 props分发到了TodoFooter
  showFilter(filter) {
    this.props.dispatch(TodoAction.showFilter(filter));
  }
  render() {
    return (
      <div style={ { width: 600, margin: '0 auto' } }>
        <TodoHeader addTodo={ this.addTodoHandle.bind(this) }></TodoHeader>
        {/* todos 通过props分发到了 TodoList */}
        <TodoList todos={ this.props.todos } toggleComplete={ this.toggleComplete.bind(this) }></TodoList>
        {/* 分发到TodoFooter的除了 filter 之外 还有 TodoConstant 常量对象 */}
        <TodoFooter filter={ this.props.filter } showFilter={ this.showFilter.bind(this) } TodoConstant={ TodoConstant }></TodoFooter>
      </div>
    );
  }
}
// 控制显示的列表
// 触发之后,state更新,因此todos的状态是更新的,但是在显示的过程中,需要根据filter去显示
const filterTodos = (todos, filter) => {
  switch (filter) {
    case TodoConstant.FILTER.SHOW_ACTIVE:
      return todos.filter((item) => {
        return !item.completed;
      });
    case TodoConstant.FILTER.SHOW_COMPLETED:
      return todos.filter((item) => {
        return item.completed;
      });
    default:
      return todos;
  }
}
// 用于connect进行state的注入
const getList = (state) => {
  console.log(state.filter);
  return {
    todos: filterTodos(state.todos, state.filter),
    filter: state.filter
  };
};
export default connect(getList)(TodoApp);

四、代码

完整的代码在github和gitosc上都拖了一份:

Github:

GitOSC: