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,来将表单与列表分开到不同的页面。

相关推荐
东方小月3 小时前
从零开发一个 Coding Agent(四):使用状态机校验大模型事件流
前端·人工智能·后端
Csvn4 小时前
🧩 ESM vs CJS 混用的 7 个「天坑」——从 TypeScript 编译到 Node 与浏览器
前端
Csvn4 小时前
🎯 Web 性能 API 集合:Performance Observer 的 5 个冷门妙用
前端
Csvn4 小时前
深入 React 闭包陷阱:从根源上理解并根治 stale closure
前端
whyfail4 小时前
前端学 Spring Boot(8):接口为什么越用越慢?
前端·spring boot·后端
用户059540174465 小时前
LangChain 记忆测试踩坑实录:这两个坑让我排查了 4 小时
前端·css
程序员黑豆5 小时前
鸿蒙应用开发:@Monitor 装饰器使用教程
前端·harmonyos
SamChan906 小时前
在Web应用中集成PDF多语言翻译功能:PDFTranslator API实战指南
前端·python·ai·pdf·yapi·机器翻译
kyriewen6 小时前
AI Agent 9秒删光了生产数据库——我给自己的项目做了5个紧急检查
前端·ai编程·claude
IT_陈寒6 小时前
JavaScript的this又双叒叕让我怀疑人生了
前端·人工智能·后端