DVA框架 todolist

背景

dva是一个基于 redux 和 redux-saga 的数据流方案,dva 还额外内置了 react-router 和 fetch。俗话说,掌握一个框架的基础就是实现一个todolist。

准备

dva-cli
[email protected]

安装dva-cli

npm install dva-cli -g

创建项目

 dva new dva-study

使用antd

npm install antd babel-plugin-import --save

编辑 .webpackrc,使 babel-plugin-import 插件生效

{
    "extraBabelPlugins": [
        ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
     ]
}

过程

1. 定义路由组件

router.js中先引入我们要做的todo组件

import TodoPage from './routes/TodoPage';

将下方的路由修改为:


在routes文件夹下,新建TodoPage.js文件,我们的todolist的整个页面都在这个组件中。

2. 设计组件结构

拆分结构,拆出的组件放入components文件夹,todolist分为两部分,第一部分是上面的表单,由输入框和一个提交按钮组成,用于输入todo,将todo提交。
第二部分是下面的todolist的展示区域,可看到一条一条的todo组成的列表
所以我们拆分成TodoForm.jsTodoList.js两个文件,放入components文件夹,TodoList.js为主todo的架子,TodoForm引入TodoList.js中,作为子组件。

3. TodoForm

TodoForm主要又Form表单构成,这个组件只是一个数据的提交,我们把提交出去的数据利用props传递给父组件

import React from 'react';
import { Form, Input, Icon, Button } from 'antd';

const TodoForm = (props) => {

  const handleSubmit = (e) => {
    e.preventDefault();
    props.form.validateFields((err, values) => {
      if (!err) {
        props.onSubmit(values);
        props.form.resetFields();
      }
    });
  };

  const { getFieldDecorator } = props.form;
  return (
    
{getFieldDecorator('todoData', { rules: [{ required: true, message: '请输入你要做的todo!' }], })( } // placeholder="Username" />, )}
); }; export default Form.create({name: 'todo_submit'})(TodoForm);

props.onSubmit(values)主要是将onSubmit方法传递给props,这样父组件可以拿到onSubmit方法。
props.form.resetFields()是在提交一个todo后,清空input用的。

4. TodoList

TodoForm.js引入TodoList.js中,使用antd中的List的组件,循环将数据展示出来。
下面是列表相关代码:

 (
         handleDel(index)}>delete]}
        >
           handleCheck(index)} checked={item.isCompleted === true ? true : false}>
            {item.data}
          
        
    )}
 />

其中,handleDel是删除todo的方法,handleCheck是完成todo的方法
新增的方法为handleAdd,绑定在TodoForm组件中:

 handleAdd(value)}/>

(这个组件在刚才List组件上面)

以为我们要在这个页面,从models中取state,所以我们要利用connect方法,将models中的state传入到组件中的props中,即:mapStateToProps方法,下面是相关代码:

const mapStateToProps = (state) => { // 这里的state的是从models中获取到的
  // console.log(state);
  return {
    todo: state.todo // 将state中的"todo"引入(model中的namespace定义的名字为"todo")
  }
}

export default connect(mapStateToProps)(TodoList) // 通过connect方法连接model

我们将dispatchprops中获取到,然后发送dispatchmodels

// 新增(异步)
  const handleAdd = ({ todoData }) => {
    dispatch({
      type: 'todo/addASync',
      payload: todoData
    })
  }

  // 删除
  const handleDel = (id) => {
    dispatch({
      type: 'todo/del',
      payload: id
    })
  }

  // 完成
  const handleCheck = (id) => {
    dispatch({
      type: 'todo/check',
      payload: id
    })
  }

为了使用到models中的effects方法,这里的新增todo的方法我们用了异步的增加,即点击之后,过一段时间才增加一条todo,这中间的延迟是我们模拟出来的,为了营造更加真实的网络环境,也为了练习dva

5. models

其实我们在开发的过程中,用到models时,我们是需要先创建一个model的,但是文章中为了条例更加通顺,我就按照大致的文件,一步一步来记录,实际过程中,会model和组件之间,来回切换开发。
model中我们创建一个namespace为todo的modelstate我们定义一个list数组,默认值为空(刚开始为空数组,因为没有数据),和一个todo是否完成的isLoading,默认值为false
Effects代表的是副作用,可以理解为,我们拿到了数据,可以在这里面进行一些操作,比如处理异步,收发请求,可以在这里调用reducer来改变state,但Effects是不能改变state的,只有reducer才能改变state
比如下面这段代码:

effects: {
    * addASync({ payload }, { call, put }) {  // eslint-disable-line
      yield put({ type: 'loading', payload: true });
      yield call(delay, 600);
      yield put({ type: 'add', payload: payload });
      yield put({ type: 'loading', payload: false });
    },
  },

其中,put方法是调用reducer,这里是调用的名为loading的reducer,这个reducer是为了改变state中的isLoading字段的,用来处理当前的loading状态,所以可以看到,第一个yield是进入loading状态,最后一行的yield是停止loading,即我们中间做完了操作后,停止loading,非常的合理。
中间的第二行,是为了模拟600毫秒的延迟,用了call方法,delay是从dva/saga里面导入的。
第三行是调用了add名字的reducer,这个reducer拿到值了以后,加入到state中的list数组中,push进去,这样todolist就增加了一条,下面是这个reducer的主要工作:

'add'(state, { payload: data }) { // payload字段是从组件中拿过来的,data就是我们要添加的内容,前面的state就是我们model中的state
    // console.log(state);
    state.list.push({
      data: data,
      isCompleted: false // 由于刚开始加入的todo还都未完成,所以我们这里的值是false
    });
    return {
      list: state.list // 返回操作后的list
    }
},

同样的道理,我们在删除完成这两个操作中,我们的方式和增加是类似的,下面是代码:

'del'(state, { payload: id }) {
    state.list.splice(id, 1);
    return {
      list: state.list
    }
},
 'check'(state, { payload: id }) {
    // console.log(state.list);
    state.list.forEach((item, index) => {
      if(index === id) {
        state.list[index].isCompleted = !state.list[index].isCompleted;
      }
    })
    return {
      list: state.list
    }
},

还有个处理loadingreducer

'loading'(state, { payload: status }) {
    // console.log(state);
    state.isLoading = status;
    return {
      list: state.list,
      isLoading: state.isLoading
    }
}

总结

可以看到,整个的数据流非常的清晰,dva为我们提供了非常好理解,便捷的方法去实现redux数据流,我们可以很清楚的知道我们在干什么,所有的数据流都在model层,不会涉及到组件中,这样就把组件的view层和data很好地拆分出来。我们在model层改变了state,数据直接会同步到我们的组件当中,非常的beautiful,非常的便捷。
目前没有在项目中使用mock,因此services这个文件夹整个下来就没有动过... 有时间把mock也整进来,这样就完美了,不然每次刷新浏览器后,我的todo都没了...555

最后来个成品图

todolist,右侧可delete这条todo

理发师Tony 2021.3.6

你可能感兴趣的:(DVA框架 todolist)