react+redux的升级版todoList的实现


Posted in Javascript onDecember 18, 2017

又是很久不写博客了,最近在用蚂蚁金服的ant-design-pro写毕设,写着写着写不下去了,很多东西都不理解,不得不说大神写出来的东西都是需要花学习成本的,或者底子好,对于React新手来说就有点难了。所以就老老实实的认真看了下Redux到底如何使用,在这里推荐一下自己最近在看的书,写的算是比较详细的:《深入React技术栈》。废话不多说,今天就分享下自己如何使用redux来实现一个todoList的,希望对想要用redux的你会有所帮助。

(为什么叫升级版呢?因为之前写过一个没有用redux的todoList)

react+redux的升级版todoList的实现

该项目使用react官方的create-react-app架构,每个目录可以根据自己的需求来划分。下面解释下每个目录的内容和功能。

public:主要放静态资源(入口html文件,图片资源,JSON文件等);

src/component:不同的组件;

src/layouts:整个页面的基本架构,主要就是Nav,Footer,Content。Nav里面显示User和Notice的数据,Content中实现页面路由的切换,Footer固定不变;

src/redux:

--src/redux/configureStore:生成整个应用的store;

--src/redux/reducers:所有reducer的集合;

src/routes:页面的整体路由;

src/utils:自己封装的工具;

views:存放项目中所要展示的所有页面;

index:整个项目的入口文件;

二. 具体实现

1. 整个应用中store中应存储什么数据?

const initialState = {
  taskListData: { //任务列表
    loading: false,
    error: false,
    taskList: [],
  }, 
  userData: { //用户信息
    loading: false,
    error: false,
    user: {},
  },
  noticeListData: { //通知列表
    loading: false,
    error: false,
    noticeList: [],
  },
  taskData: { //任务详情,在详情页使用
    loading: false,
    error: false,
    task: {},
  }
};

2. reducer的分布:

每个state对应一个reducer,所以一共需要四个reducer,在src/redux/reducers中将所有的reducer合并,并且注意每个reducer的名字要和state同名:

/*redux/reducers.js*/
import { combineReducers } from 'redux';
import userReducer from '../component/User/indexRedux';
import noticeReducer from '../component/Notice/indexRedux';
import todoListReducer from '../views/TodoList/indexRedux';
import taskReducer from '../views/Detail/indexRedux';

export default combineReducers({
  userData: userReducer,
  noticeListData: noticeReducer, 
  taskListData: todoListReducer,
  taskData: taskReducer,
});

每个state都对应一个reducer,所以和state一样,reducer应在放在最顶级的父级组件的目录中,所以将taskListData的reducer放在src/views/TodoList中,其他同理,代码如下:

/*views/TodoList/indexRedux.js*/
const taskListData = {
  loading: true,
  error: false,
  taskList: []
};
//不同的action;
const LOAD_TASKLIST = 'LOAD_TASKLIST';
const LOAD_TASKLIST_SUCCESS = 'LOAD_TASKLIST_SUCCESS';
const LOAD_TASKLIST_ERROR = 'LOAD_TASKLIST_ERROR';
const ADD_TASK = 'ADD_TASK';
const UPDATE_TASK = 'UPDATE_TASK';
const DELETE_TASK = 'DELETE_TASK';
function todoListReducer (state = { taskListData }, action) {
  switch(action.type) {
    case LOAD_TASKLIST: {
      return {
        ...state,
        loading: true,
        error: false,
      }
    }
    case LOAD_TASKLIST_SUCCESS: {
      return {
        ...state,
        loading: false,
        error: false,
        taskList: action.payload,
      };
    }
    case LOAD_TASKLIST_ERROR: {
      return {
        ...state,
        loading: false,
        error: true
      };
    }
    case UPDATE_TASK: {
      const index = state.taskList.indexOf(
        state.taskList.find(task => 
          task.id === action.payload.id));
      console.log(index);
      state.taskList[index].status = !state.taskList[index].status;
      return {
        ...state,
        taskList: state.taskList,
      };
    }
    case DELETE_TASK: {
      const index = state.taskList.indexOf(
        state.taskList.find(task => 
          task.id === action.payload.id));
      state.taskList.splice(index, 1);
      return {
        ...state,
        taskList: state.taskList,
      };
    }
    case ADD_TASK: {
      let len = state.taskList.length;
      let index = len > 0 ? len - 1 : 0;
      let lastTaskId = index !== 0 ? state.taskList[index].id : 0; 
      state.taskList.push({
        id: lastTaskId + 1,
        name: action.payload.name,
        status: false,
      });
      return {
        ...state,
        taskList: state.taskList,
      }
    } 
    default: {
      return state;
    }
  }
}
export default todoListReducer;

3. action creator的分布:

每个动作都代表一个action,action由组件发出,所以将action creator单独一个文件,放在组件目录中。例如:ListItem组件的action creator:

/*ListItem/indexRedux.js*/
//处理更新任务状态后和删除任务后的taskList的状态;
const UPDATE_TASK = 'UPDATE_TASK';
const DELETE_TASK = 'DELETE_TASK';
//action creator,更新和删除任务
export function updateTask (task) {
  return dispatch => {
    dispatch({
      type: UPDATE_TASK,
      payload: task,
    });
  }
}
export function deleteTask (task) {
  return dispatch => {
    dispatch({
      type: DELETE_TASK,
      payload: task,
    });
  }
}

三. 如何将redux和组件连接

react-redux提供了connect方法,将state和action creator绑在组件上,然后在组价内部以props的方式获取。下面是TodoList页面的具体实现:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import List from '../../component/List';
import { loadTaskList } from '../../component/List/indexRedux';
import { updateTask, deleteTask } from '../../component/ListItem/indexRedux';
import { addTask } from '../../component/SubmitDialog/indexRedux';
class TodoList extends Component {

  render () {
    return (
      <List {...this.props} />
    );
  }
}
export default connect( state => {
  return {
    loading: state.taskListData.loading,
    error: state.taskListData.error,
    taskList: state.taskListData.taskList,
  };
}, dispatch => {
  return {
    loadTaskList: bindActionCreators(loadTaskList, dispatch),
    updateTask: bindActionCreators(updateTask, dispatch),
    deleteTask: bindActionCreators(deleteTask, dispatch),
    addTask: bindActionCreators(addTask, dispatch),
  };
})(TodoList);

connect方法有四个参数,这里主要说下前两个参数:

(1)mapStateToProps:参数为state,返回页面所需要的所有state;

(2)mapDispatchToProps:参数为dispatch,返回页面所要使用的异步回调函数。

眼明手快的你肯定看到了,我们从redux包中导出了bindActionCreators方法,该方法将dispatch和action creator绑定,用来触发action。

四. 异步的action creator如何触发呢?

因为每个action creator都是异步函数,我们传给组件的只是函数的声明,所以就要引入我们的中间件,只用在生成store时加入就行了:

/*redux/configureStore.js*/
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
const initialState = {
  taskListData: {
    loading: false,
    error: false,
    taskList: [],
  }, 
  userData: {
    loading: false,
    error: false,
    user: {},
  },
  noticeListData: {
    loading: false,
    error: false,
    noticeList: [],
  },
  taskData: {
    loading: false,
    error: false,
    task: {},
  }
};
let enhancer = applyMiddleware(thunk);
let store = createStore(
  reducers,
  initialState,
  enhancer,
);
export default store;

在上面的代码中thunk就是一个中间件,我们将引入的中间件传入applyMiddleware就可以了。

五. store在哪里传入组件呢?

我们肯定会想到,store在整个应用中都存在,所以应该在整个应用的最顶层,对于一般项目而言,当然就是最顶端的路由了:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import BasicLayout from '../layouts';
import store from '../redux/configureStore';
class RouterApp extends Component {
  render () {
    return (
      <Provider store={store}>
        <Router>
          <Route path="/" component={BasicLayout} />
        </Router>
      </Provider>
    );
  }
}
export default RouterApp;

Provider是react-redux的一个组件,作用就是用来将store传入整个应用。

基本要讲的就是这些内容,完整的项目请看 github
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery1.4后 jqDrag 拖动 不可用
Feb 06 Javascript
Jquery中显示隐藏的实现代码分析
Jul 26 Javascript
《JavaScript高级程序设计》阅读笔记(一) ECMAScript基础
Feb 27 Javascript
jQuery带进度条全屏图片轮播特效代码分享
Jun 28 Javascript
深入理解JQuery中的事件与动画
May 18 Javascript
footer定位页面底部(代码分享)
Mar 07 Javascript
Vuex的基本概念、项目搭建以及入坑点
Nov 04 Javascript
Vue插槽原理与用法详解
Mar 05 Javascript
JS实现获取当前所在周的周六、周日示例分析
May 11 Javascript
在pycharm中开发vue的方法步骤
Mar 04 Javascript
Vue 实现v-for循环的时候更改 class的样式名称
Jul 17 Javascript
谈谈JavaScript中的垃圾回收机制
Sep 17 Javascript
总结js中的一些兼容性易错的问题
Dec 18 #Javascript
详解最新vue-cli 2.9.1的webpack存在问题
Dec 16 #Javascript
HTML5+JS+JQuery+ECharts实现异步加载问题
Dec 16 #jQuery
详解如何使用 vue-cli 开发多页应用
Dec 16 #Javascript
详解VUE 数组更新
Dec 16 #Javascript
详解如何用模块化的方式写vuejs
Dec 16 #Javascript
浅谈 Vue 项目优化的方法
Dec 16 #Javascript
You might like
PHP根据传入参数合并多个JS和CSS文件的简单实现
2014/06/13 PHP
PHP闭包实例解析
2014/09/08 PHP
ecshop后台编辑器替换成ueditor编辑器
2015/03/03 PHP
PHP实现生成唯一会员卡号
2015/08/24 PHP
php 无限级分类 获取顶级分类ID
2016/03/13 PHP
Laravel框架使用monolog_mysql实现将系统日志信息保存到mysql数据库的方法
2018/08/16 PHP
初识JQuery 实例一(first)
2011/03/16 Javascript
jQuery1.6 类型判断实现代码
2011/09/01 Javascript
jquery1.83 之前所有与异步列队相关的模块详细介绍
2012/11/13 Javascript
js 操作select与option(示例讲解)
2013/12/20 Javascript
JavaScript中一个奇葩的IE浏览器判断方法
2014/04/16 Javascript
javascript trim函数在IE下不能用的解决方法
2014/09/12 Javascript
深入理解JavaScript系列(17):面向对象编程之概论详细介绍
2015/03/04 Javascript
javascript简单判断输入内容是否合法的方法
2016/05/11 Javascript
window.open不被拦截的简单实现代码(推荐)
2016/08/04 Javascript
用vue和node写的简易购物车实现
2017/04/25 Javascript
Angular中封装fancyBox(图片预览)遇到问题小结
2017/09/01 Javascript
详解vuex中mapState,mapGetters,mapMutations,mapActions的作用
2018/04/13 Javascript
Vue EventBus自定义组件事件传递
2018/06/25 Javascript
jQuery实现的简单拖拽功能示例【测试可用】
2018/08/14 jQuery
vue.js+ElementUI实现进度条提示密码强度效果
2020/01/18 Javascript
python操作ie登陆土豆网的方法
2015/05/09 Python
bat和python批量重命名文件的实现代码
2016/05/19 Python
Python读取一个目录下所有目录和文件的方法
2016/07/15 Python
Django Admin 实现外键过滤的方法
2017/09/29 Python
Python常用模块sys,os,time,random功能与用法实例分析
2020/01/07 Python
阿玛尼美妆俄罗斯官网:Giorgio Armani Beauty RU
2020/07/19 全球购物
波兰办公用品和学校用品在线商店:Dlabiura24.pl
2020/11/18 全球购物
Internal修饰符有什么含义
2013/07/10 面试题
实习自荐信
2013/10/13 职场文书
社会保险接收函
2014/01/12 职场文书
代理协议书
2014/04/22 职场文书
文明美德伴我成长演讲稿
2014/05/12 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
银行招聘自荐信
2015/03/06 职场文书
实例讲解Python中sys.argv[]的用法
2021/06/03 Python