React入门第二章:用户管理功能实战入手

1,268 阅读5分钟

Vue组件和React组件的区别

Vue组件是创建.vue文件,通过vue-loader转换成js文件。
React组件是创建.jsx文件,通过babel-loader转换成js文件。

Vue组件通过templatescriptstyle标签的形式区分模板、逻辑和样式
React组件直接在内部写逻辑,最后导出jsx模板。

Vue支持在main.js中直接全局引入组件库。
React在需要使用的地方手动引入组件。

Vue支持局部样式,style标签添加scoped属性
React需要使用CSS Modules的方式使用局部样式

Vue通过v-ifv-for指令处理渲染内容
React通过js的iffor原生语法处理渲染内容

Vue11个生命周期钩子函数用于处理各种情况。
实例初始化之后:beforeCreate
实例创建完成后:created
挂载开始之前:beforeMount
实例被挂载后:mounted
DOM 被更新之前:beforeUpdate
DOM 更新完之后:updated
实例销毁之前:beforeDestroy
实例销毁之后:destroyed
缓存的组件激活时:activated
缓存的组件失活时:deactivated
捕获后代组件的错误时:errorCaptured

React也提供了几乎差不多的生命周期钩子函数
组件将要挂载时:componentWillMount
组件挂载完成时:componentDidMount
是否要更新数据时:shouldComponentUpdate
数据更新完成时:componentDidUpdate
当组件从DOM中移除时:componentWillUnmount
当抛出错误时:componentDidCatch
将要更新数据时:componentWillUpdate 即将废弃
父组件中改变了props传值时:componentWillReceiveProps 即将废弃

通过用户管理理解JSX和函数组件

学习最好的方式就是实战,什么都别说了,就是干!

jsx

JSX是一个 JavaScript 的语法扩展JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。

可以将JSX简单理解为模板和JS的结合,继承了JS的灵活性。

JSX绑定属性通过花括号绑定,Vue通过v-bind指令或冒号绑定属性。

关于JSX可以阅读React-JSX简介文档,两分钟就能上手。

用户列表

函数组件就是一个纯函数,返回JSX即可。

添加用户管理的列表组件并添加路由配置

// config/routes.js
export default [
  {
    path: '/access',
    name: '权限管理',
    routes: [
      { path: '/access/user', name: '用户管理', component: './access/User' }
    ]
  },
]
// src/pages/access/User.jsx
export default () => {
  return <div>用户管理</div>
}

检测页面是否正常显示。若不正常检查问题,

此处我出现问题,主要提前在函数外面定义了数据,删除后页不正常,通过删除.umi目录后重新启动后才正常,估计是缓存原因

使用pro-layout的PageContainer和antd的Table组件将页面完善。

import { PageContainer } from '@ant-design/pro-layout';
import { Table, Space, Button } from 'antd';
export default () => {
  const sexs = ['男', '女'];
  const columns = [
    { dataIndex: 'id', title: 'ID' },
    { dataIndex: 'name', title: '用户姓名' },
    { dataIndex: 'sex', title: '性别', render: (text, record, index) => sexs[text] },
    { dataIndex: 'age', title: '年龄' },
    {
      dataIndex: 'action',
      title: '操作',
      width: 150,
      render: () => (
        <Space size="middle">
          <a>编辑</a>
          <a>详情</a>
          <a>删除</a>
        </Space>
      ),
    },
  ];
  const datas = [
    { id: 1, name: '张三', age: 19, sex: 0 },
    { id: 2, name: '李四', age: 22, sex: 1 },
  ];
  return (
    <PageContainer
      title="用户管理"
      header={{
        extra: [
          <Button key="1" type="primary">
            创建用户
          </Button>,
        ],
      }}
    >
      <Table columns={columns} dataSource={datas} rowKey="id" />
    </PageContainer>
  );
};

columns中的render用于转换数据,和iView的render类似。

注意Table组件添加RowKey属性,Button组件的key属性。
否则控制台报错,这点和Vue的其他组件库不一样。

用户表单

我个人不喜欢把表单和列表写在一起,所以表单需要单独创建一个组件。正好熟悉一下函数式组件间传值

import React, { useImperativeHandle } from 'react';
import { Form, Input, InputNumber, Radio } from 'antd';
import { useEffect } from 'react';
export default React.forwardRef(({ data }, ref) => {
  const [form] = Form.useForm();

  // 自定义暴露给父组件的实例值
  useImperativeHandle(ref, () => ({
    validate: form.validateFields,
    resetFields: form.resetFields,
  }));

  // 函数组件的生命周期
  useEffect(() => {
    if (data && data.id) {
      form.setFieldsValue(data);
    }
  });

  // JSX渲染
  return (
    <Form form={form} ref={ref}>
      <Form.Item name="name" label="姓名" rules={[{ required: true }]}>
        <Input placeholder="请输入用户姓名" />
      </Form.Item>
      <Form.Item name="sex" label="性别" rules={[{ required: true }]}>
        <Radio.Group placeholder="请选择用户性别">
          <Radio value={0}></Radio>
          <Radio value={1}></Radio>
        </Radio.Group>
      </Form.Item>
      <Form.Item name="age" label="年龄" rules={[{ required: true }]}>
        <InputNumber />
      </Form.Item>
    </Form>
  );
});

React.forwardRef用于函数组件的实例暴露。由于表单组件是单独的组件,需要在父级组件获取子级组件的数据。

useEffect是React Hook方法之一 函数组件没有生命周期,Effect Hook可以让你在函数组件中执行副作用操作,类似生命周期。

列表事件触发表单组件

表单使用抽屉组件承载,通过列表页事件显示表单。

添加操作方法、Drawer抽屉数据对象和控制Drawer抽屉显示/隐藏的数据。

之前datas数据是静态的,表单添加/修改需要改变datas,datas也需要改为可修改的状态数据

import { useState } from 'react';
import { Table, Space, Button, Drawer } from 'antd';

export default () => {
  ...
  /**
   * 数据
   */
  const [datas, setDatas] = useState([
    { id: 1, name: '张三', age: 19, sex: 0 },
    { id: 2, name: '李四', age: 22, sex: 1 },
  ]);
  // 抽屉控制
  const [showDrawer, setShowDrawer] = useState(false);
  // 抽屉数据
  const [drawerData, setDrawerData] = useState({});
  
  /**
   * 操作
   */

  // 显示表单
  const handleShowForm = (data) => {
    if (data) {
      setDrawerData(data);
    }
    setShowDrawer(true);
  };

  // 关闭操作
  const handleCancel = () => {
    setShowDrawer(false);
    setDrawerData({});
    userForm.current.resetFields();
  };
  
  // 保存操作
  const handleSave = async () => {
    const data = await userForm.current.validate();
    if (drawerData.id) {
      const index = datas.findIndex((v) => v.id === drawerData.id);
      datas.splice(index, 1, { ...data, id: drawerData.id });
    } else {
      datas.push({ ...data, id: datas.length + 1 });
    }
    setDatas([...datas]);
    handleCancel();
  };
  
  /**
   * JSX渲染
   */
  return (
    <PageContainer
      title="用户管理"
      header={{ extra: [<Button key="1" type="primary" onClick={handleShowForm}>创建用户</Button>], }}
    >
      <Table columns={columns} dataSource={datas} rowKey="id" />
      <Drawer
        title="创建用户"
        width={500}
        maskClosable={false}
        visible={showDrawer}
        onClose={handleCancel}
      >
        <UserForm data={drawerData} ref={userForm} />
        <Space size="middle">
          <Button onClick={handleCancel}>关闭</Button>
          <Button type="primary" onClick={handleSave}>保存</Button>
        </Space>
      </Drawer>
    </PageContainer>
  );
}

useState是React的hook的方法之一
函数组件是没有状态的,也就是说函数组件不会监听到数据的变化,而Hook就是为了解决这个问题而生的(暂时这么理解)
用法类似Vue中的store,状态不可更改,通过设置状态的方法更改状态。
注意const后面是数组的中括号[]

完善删除和详情操作

详情操作继续用Drawer展示,和表单类似,不再继续了。

删除操作用antd的Popconfirm组件实现拦截提示即可

...
<Popconfirm
  title="您确定要删除当前数据么?"
  onConfirm={() => {
    handleRemove(record);
  }}
>
  <a>删除</a>
</Popconfirm>
...

// 删除操作
const handleRemove = (data) => {
  const index = datas.findIndex((v) => v.id === data.id);
  datas.splice(index, 1);
  setDatas([...datas]);
  message.success('删除成功!');
};

结语

  1. 通过用户的增删改查操作,大致摸清楚了React的函数组件的实现方式。
  2. 通过使用Ant Design组件库,对React组件库有了基本的了解,同时熟悉了JSX语法。
  3. 拆分组件后理解了函数组件的ref获取实例的方式以及子组件控制暴露API的方式
  4. 初步了解了React Hook的使用方式和基本作用。