AI 编程时代手工匠人代码打造 React 项目实战 (二):引入组件库 Antd

前置工作

使用 pnpm i antd @ant-design/icons 安装 antd 和它提供的图标库来作为我们项目的 UI 库。

编码流程

1.修改 Table 组件

我们参照 antd 的官方文档,定义一个 columns 来描述每一列。

tsx 复制代码
//App.tsx
const columns = [
  {
    title: '用户ID',
    dataIndex: 'userId',
    key: 'userId',
  },
  {
    title: '姓名',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: '年龄',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: '所在地',
    dataIndex: 'city',
    key: 'city',
  },
  {
    title: '角色名称',
    dataIndex: 'role',
    key: 'role',
  },
  {
    title: '操作',
    dataIndex: 'operation',
    key: 'operation',
  },
]
//...
return (
  //...
  <Table columns={columns} dataSource={filterData} />
)

现在 Table 组件已经能够正常显示数据了,下一步我们将原有的操作的按钮和编辑模式加上,查阅文档我们可以发现 Column 提供了一个 render 的接口,用来定义每一列的渲染逻辑,入参形式为(当前单元格的值,当前行数据,行索引) => {} ,我们根据之前的代码逻辑来改写对应的列定义。

tsx 复制代码
//App.tsx
const columns: TableProps<User>["columns"] = [
  {
    title: "姓名",
    dataIndex: "name",
    key: "name",
    render: (_, record) => {
      return (
        <>
          {editMap[record.userId] ? (
            <input
              ref={(node) => {
                const map = getMap();
                map.set(record.userId, node as HTMLInputElement);
                return () => {
                  map.delete(record.userId);
                };
              }}
              value={editMap[record.userId].name}
              onChange={(e) => handleChange(e, record.userId)}
            />
          ) : (
            <span>{record.name}</span>
          )}
        </>
      );
    },
  },
  //...
  {
    title: "操作",
    dataIndex: "operation",
    key: "operation",
    render: (_, record) => {
      return (
        <>
          {editMap[record.userId] ? (
            <button onClick={() => handleConfirmEdit(record.userId)}>
              保存
            </button>
          ) : (
            <button onClick={() => handleEdit(record.userId)}>修改</button>
          )}
          <button onClick={() => handleDelete(record.userId)}>删除</button>
        </>
      );
    },
  },
];
2. 修改 Form 组件

使用 Form 组件的时候,如果是用过 Vue 的同学可能会感觉到有点别扭。一般在 Vue 中我们会定义一个 formData 的响应式对象来收集数据,然后在控件上使用 v-model 来控制控件的值。而查看 Antd 的 Form 组件示例代码,会发现它完全没有在控件上传入 value 或是 onChange 这样的字段,Form 组件内集中管理了表单控件的状态,并且向外暴露 useForm hook 让我们可以获取状态或者操作它们。

tsx 复制代码
// App.tsx
function UserForm({ onAddUser }: { onAddUser: (user: UserFormData) => void }) {
  const [form] = Form.useForm();
​
  const handleReset = () => {
    form.resetFields();
  };
​
  const handleSubmit = async () => {
    // 触发校验
    await form.validateFields()
    // 获取表单的值
    const formData = form.getFieldsValue();
    onAddUser({
      ...formData,
      age: Number(formData.age),
    });
    // 添加之后 reset
    handleReset()
  };
​
  return (
    <>
      <Form
        form={form}
        labelCol={{ span: 8 }}
        wrapperCol={{ span: 16 }}
        style={{ maxWidth: 600 }}
        initialValues={defaultFormData}
        autoComplete="off"
      >
        <Form.Item<UserFormData>
          label="姓名"
          name="name"
          rules={[{ required: true, message: "请输入姓名" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item<UserFormData>
          label="年龄"
          name="age"
          rules={[{ required: true, message: "请输入年龄" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item<UserFormData>
          label="所在地"
          name="city"
          rules={[{ required: true }]}
        >
          <Select>
            <Select.Option value={"深圳"}>深圳</Select.Option>
            <Select.Option value={"广州"}>广州</Select.Option>
          </Select>
        </Form.Item>
        <Form.Item<UserFormData>
          label="角色"
          name="role"
          rules={[{ required: true }]}
        >
          <Select>
            <Select.Option value={"销售"}>销售</Select.Option>
            <Select.Option value={"销售经理"}>销售经理</Select.Option>
          </Select>
        </Form.Item>
​
        <Form.Item label={null}>
          <button type="button" onClick={handleSubmit}>
            添加
          </button>
          <button type="button" onClick={handleReset}>
            重置
          </button>
        </Form.Item>
      </Form>
    </>
  );
}

验证一下代码功能,发现和我们预期的一样。handleSubmit 其实还能够简化, Form 组件会监听 onSubmit ,在校验成功后触发一个名为 onFinished 事件。

tsx 复制代码
// App.tsx
const handleSubmit = (values: UserFormData) => {
  onAddUser({
    ...values,
    age: Number(values.age),
  });
  // 添加之后 reset
  handleReset()
};
//...
<Form
  //...
  onFinish={handleSubmit}
>
  //...
  <Form.Item label={null}>
    // 修改 type 为 submit,触发表单的 submit 事件
    <button type="submit">
      添加
    </button>
    <button type="button" onClick={handleReset}>
      重置
    </button>
  </Form.Item>
</Form>

小结

我们将原生 HTML 标签替换为了常用 UI 库的组件,并且发现了一些它设计理念上与 Vue 实践中的差异,接下来我们将引入 React Router,来将表单与列表分开到不同的页面。

相关推荐
Ali酱2 分钟前
远程这两年,我才真正感受到——工作,原来可以不必吞噬生活。
前端·面试·远程工作
金金金__7 分钟前
优化前端性能必读:浏览器渲染流程原理全揭秘
前端·浏览器
Data_Adventure11 分钟前
Vue 3 手机外观组件库
前端·github copilot
泯泷17 分钟前
Tiptap 深度教程(二):构建你的第一个编辑器
前端·架构·typescript
屁__啦23 分钟前
前端错误-null结构
前端
lichenyang45323 分钟前
从0开始的中后台管理系统-5(userList动态展示以及上传图片和弹出创建用户表单)
前端
未来之窗软件服务28 分钟前
解析 div 禁止换行与滚动条组合-CSS运用
前端·css
不远处的小阿秋1 小时前
2025年,前端还需要虚拟DOM吗
前端
DcTbnk1 小时前
tailwindcss、postcss、autoprefixer,这三个分别是干嘛的
前端
zReadonly1 小时前
antdv@4.x在360极速浏览器兼容解决办法
前端