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

相关推荐
Patrick_Wilson12 分钟前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI38 分钟前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl1 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影1 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海1 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo1 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州1 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
星栈1 小时前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
在因斯坦1 小时前
摸摸鱼之前端自己研究 Jenkins 自动化部署
前端
snpgroupcn2 小时前
SAP实施风险管控注意事项:四大风险域与防范策略
前端·数据库·人工智能