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 相关文章推荐
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
Apr 10 Javascript
js实现select组件的选择输入过滤代码
Oct 14 Javascript
推荐9款炫酷的基于jquery的页面特效
Dec 07 Javascript
jQuery内部原理和实现方式浅析
Feb 03 Javascript
node.js调用Chrome浏览器打开链接地址的方法
May 17 Javascript
react-native android状态栏的实现
Jun 15 Javascript
jQuery插件实现弹性运动完整示例
Jul 07 jQuery
vue单页缓存存在的问题及解决方案(小结)
Sep 25 Javascript
详解vue修改elementUI的分页组件视图没更新问题
Nov 13 Javascript
jQuery实现手风琴特效
Jan 11 jQuery
利用js实现简单开关灯代码
Nov 23 Javascript
分享一个vue实现的记事本功能案例
Apr 11 Vue.js
总结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
星际争霸 Starcraft 游戏介绍
2020/03/14 星际争霸
一个更简单的无限级分类菜单代码
2007/01/16 PHP
手把手教你使用DedeCms V3的在线采集图文教程
2007/04/03 PHP
Linux下将excel数据导入到mssql数据库中的方法
2010/02/08 PHP
PHP中exec函数和shell_exec函数的区别
2014/08/20 PHP
纯php生成随机密码
2015/10/30 PHP
YII Framework框架教程之国际化实现方法
2016/03/14 PHP
yii2 commands模式以及配置crontab定时任务的方法
2017/08/19 PHP
javascript得到XML某节点的子节点个数的脚本
2008/10/11 Javascript
javascript 时间比较实现代码
2009/10/28 Javascript
IE本地存储userdata的一个bug说明
2010/07/01 Javascript
JavaScript二维数组实现的省市联动菜单
2014/05/08 Javascript
原生js实现放大镜
2017/02/20 Javascript
详解vue父子模版嵌套案例
2017/03/04 Javascript
微信小程序 下拉列表的实现实例代码
2017/03/08 Javascript
SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题的解决方法
2018/01/09 Javascript
Angular 5.x 学习笔记之Router(路由)应用
2018/04/08 Javascript
js字符串处理之绝妙的代码
2019/04/05 Javascript
vue中$refs, $emit, $on, $once, $off的使用详解
2019/05/26 Javascript
Vue退出登录时清空缓存的实现
2019/11/12 Javascript
JavaScript中的函数申明、函数表达式、箭头函数
2019/12/06 Javascript
python的re模块应用实例
2014/09/26 Python
Python的subprocess模块总结
2014/11/07 Python
在Django中创建动态视图的教程
2015/07/15 Python
Python循环语句之break与continue的用法
2015/10/14 Python
Python中str is not callable问题详解及解决办法
2017/02/10 Python
用python 批量更改图像尺寸到统一大小的方法
2018/03/31 Python
python内存动态分配过程详解
2019/07/15 Python
Python基于Twilio及腾讯云实现国际国内短信接口
2020/06/18 Python
HTML5中的Web Notification桌面通知功能的实现方法
2019/07/29 HTML / CSS
高三毕业生自我鉴定
2013/12/20 职场文书
缴纳养老保险的证明
2014/01/10 职场文书
大学生精神文明先进个人事迹材料
2014/05/02 职场文书
学校领导班子对照检查材料
2014/09/24 职场文书
4S店收银员岗位职责
2015/04/07 职场文书
《狼牙山五壮士》读后感:宁死不屈,视死如归
2019/08/16 职场文书