返回

DvaJS入门指南:详解umi2 + dva模式下的用户管理CURD应用构建

前端

前言

最近一个月来,我一直在使用 dva 对公司存量项目进行重构,因此没有多少时间写文章。随着9月开学季的到来,我最近一直在使用的几个开源项目都迎来了重大更新。首先,umi 终于迎来了 2.0 版本,有关详细信息,请查看发布 umi 2.0,可插拔的企业级 react 应用框架。随之而来的是使用 umi 开发的新项目都必须使用 dva 版本 2.0 及以上。

项目搭建

首先,我们先创建一个新的umi项目,在命令行中输入以下命令:

npx create-umi my-app

然后,进入项目目录,安装 dva:

cd my-app
npm install dva dva-model

数据模型

接下来,我们需要定义我们的数据模型。在 src/models/user.js 中,我们创建了一个名为 user 的模型:

import { defineModel } from 'dva';

export default defineModel({
  namespace: 'user',
  state: {
    list: [],
    total: 0,
    page: 1,
    pageSize: 10,
  },
  effects: {
    *fetchUsers({ payload }, { call, put }) {
      const response = yield call(fetch, '/api/users', {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      });
      const data = yield response.json();
      yield put({
        type: 'saveUsers',
        payload: data,
      });
    },
    *createUser({ payload }, { call, put }) {
      const response = yield call(fetch, '/api/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      });
      const data = yield response.json();
      yield put({
        type: 'addUser',
        payload: data,
      });
    },
    *updateUser({ payload }, { call, put }) {
      const response = yield call(fetch, `/api/users/${payload.id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      });
      const data = yield response.json();
      yield put({
        type: 'modifyUser',
        payload: data,
      });
    },
    *deleteUser({ payload }, { call, put }) {
      const response = yield call(fetch, `/api/users/${payload.id}`, {
        method: 'DELETE',
      });
      yield put({
        type: 'removeUser',
        payload: payload,
      });
    },
  },
  reducers: {
    saveUsers(state, { payload }) {
      return {
        ...state,
        list: payload.data,
        total: payload.total,
      };
    },
    addUser(state, { payload }) {
      return {
        ...state,
        list: [...state.list, payload],
      };
    },
    modifyUser(state, { payload }) {
      const index = state.list.findIndex(item => item.id === payload.id);
      return {
        ...state,
        list: [
          ...state.list.slice(0, index),
          payload,
          ...state.list.slice(index + 1),
        ],
      };
    },
    removeUser(state, { payload }) {
      return {
        ...state,
        list: state.list.filter(item => item.id !== payload.id),
      };
    },
  },
});

路由配置

接下来,我们需要配置路由。在 src/routes.js 中,我们添加以下代码:

export default [
  {
    path: '/users',
    component: '@/pages/users/index',
  },
];

组件编写

现在,我们可以编写组件了。在 src/pages/users/index.js 中,我们创建了一个名为 Users 的组件:

import { connect } from 'dva';
import { Table, Button, Modal, Form, Input } from 'antd';

const Users = ({ dispatch, loading, list, total, page, pageSize }) => {
  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
      key: 'id',
    },
    {
      title: '姓名',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: '年龄',
      dataIndex: 'age',
      key: 'age',
    },
    {
      title: '操作',
      key: 'action',
      render: (text, record) => (
        <span>
          <Button type="primary" onClick={() => handleEdit(record)}>编辑</Button>
          <Button type="danger" onClick={() => handleDelete(record)}>删除</Button>
        </span>
      ),
    },
  ];

  const [form] = Form.useForm();

  const [visible, setVisible] = useState(false);

  const handleEdit = (record) => {
    setVisible(true);
    form.setFieldsValue(record);
  };

  const handleDelete = (record) => {
    dispatch({
      type: 'user/deleteUser',
      payload: record,
    });
  };

  const handleOk = () => {
    form
      .validateFields()
      .then((values) => {
        if (values.id) {
          dispatch({
            type: 'user/updateUser',
            payload: values,
          });
        } else {
          dispatch({
            type: 'user/createUser',
            payload: values,
          });
        }
        setVisible(false);
        form.resetFields();
      })
      .catch((err) => {
        console.log(err);
      });
  };

  const handleCancel = () => {
    setVisible(false);
    form.resetFields();
  };

  const pagination = {
    total,
    current: page,
    pageSize,
    onChange: (page, pageSize) => {
      dispatch({
        type: 'user/fetchUsers',
        payload: {
          page,
          pageSize,
        },
      });
    },
  };

  return (
    <>
      <Button type="primary" onClick={() => setVisible(true)}>新建用户</Button>
      <Table columns={columns} dataSource={list} loading={loading} pagination={pagination} />
      <Modal
        title="编辑用户"
        visible={visible}
        onOk={handleOk}
        onCancel={handleCancel}
      >
        <Form form={form}>
          <Form.Item name="id" hidden={true}>
            <Input />
          </Form.Item>
          <Form.Item label="姓名" name="name" rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          <Form.Item label="年龄" name="age" rules={[{ required: true }]}>
            <Input />
          </Form.Item>
        </Form>
      </Modal>
    </>
  );
};

export default connect(({ user, loading }) => ({
  list: user.list,
  total: user.total,
  page: user.page,
  pageSize: user.pageSize,
  loading: loading.effects['user/fetchUsers'],
}))(Users);

服务端接口开发

最后,我们需要开发服务端接口。在 src/server/index.js 中,我们添加以下代码:

const express = require('express');
const app = express();
const port = 3000;

app.get('/api/users', async (req, res) => {
  const users = await User.find();
  res.json({
    data: users,
    total: users.length,
  });
});

app