React 最佳实践:动态表单| 8月更文挑战

具体场景

基于 Ant Design, 咱们来实现一个【薪资调查表】,需求有二:html

  • 职位(Job)列表 模拟从服务端拉取 form-2.pngreact

  • 当职位是 Student 时,薪资(Income) 输入框消失。 form-3.pngweb

    form-4.png

实现

UI 描述

form-1.png

咱们先用 Ant Design 提供的 Form, Input, Select, Button 组件来构建 UI 架构。数组

  • labelCollabel 标签布局,wrapperCol为输入控件设置布局样式,使用方式同 Grid 栅格markdown

  • 使用 <Button htmlType="submit" /> 调用 web 原生提交逻辑。antd

import { Form, Input, Select, Button } from 'antd';
const { Option } = Select;

const layout = {
  labelCol: { span: 6 },
  wrapperCol: { span: 12 },
};

const DynamicForm = () => {
  return (
    <Form {...layout}> <Form.Item name="name" label="User Name"> <Input /> </Form.Item> <Form.Item name="job" label="Job"> <Select placeholder="Select a option and change input text above"> <Option>loading</Option> </Select> </Form.Item> <Form.Item name="income" label="Income"> <Input /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit"> Submit </Button> </Form.Item> </Form>
  );
};

export default DynamicForm;
复制代码

基础 UI 框架完成后,咱们来实现开头的两个逻辑需求。架构

模拟服务端拉取列表

先介绍一下 useState 和 useEffect 两个 Hooks 吧:app

useState

const [state, setState] = useState(initialState);
复制代码

它会返回一个 state,以及更新 state 的函数。框架

initialState 会做为 state 的初始值。函数

useEffect

useEffect(didUpdate);
复制代码

该 Hook 接收一个包含命令式、且可能有反作用代码的函数。

赋值给 useEffect 的函数会在组件渲染到屏幕以后执行,改变 DOM、添加订阅、设置定时器、记录日志以及执行其余包含反作用的操做,能够放在 useEffect 里执行。

组件卸载时须要清除 effect 建立的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数,咱们在下面也会用到。

默认状况下,effect 会在每轮组件渲染完成后执行。然而,咱们只想在某些特定条件下执行 effect,该怎么办呢?

要实现这一点,能够给 useEffect 传递第二个参数,只有当 props.source 改变后才会从新建立订阅。

useEffect(didiUpdate, [props.source]);
复制代码

实现

import { useState, useEffect } from 'react';

const DynamicForm = () => {
  const [jobs, setJobs] = useState([]);

  useEffect(() => {
    const timer = setTimeout(() => {
      setJobs(['engineer', 'student', 'doctor']);
    }, 2000);
    return function clear() {
      clearTimeout(timer);
    };
  }, []); // [],只执行一次

  return;
};
复制代码

在列表数据拉取完成以后,遍历 jobs 渲染下拉项:

<Select>
  {jobs.length > 0 ? (
    jobs.map((job) => (
      <Option key={job} value={job}> {job} </Option>
    ))
  ) : (
    <Option>loading</Option>
  )}
</Select>
复制代码

动态表单元素

Job 字段值为 Student 时,Income 输入框须要隐藏。这样的话,咱们须要能获取 Job 的字段值。

在函数组件中,经过 Form.useForm 对表单数据域进行交互。getFieldValue 方法能够获取对应字段名的值。

const DynamicForm = () => {
  const [form] = Form.useForm();
  return (
    <Form form={form} > {({ getFieldValue }) => getFieldValue('job') !== 'student' ? ( <Form.Item name="income" label="Income" > <Input /> </Form.Item> ) : null } </Form>
};
复制代码

咱们但愿在修改 Job 字段值后更新 Income 输入控件的显隐,shouldUpdate 能够帮助咱们实现这个更新逻辑。

<Form.Item
  shouldUpdate={(prevValues, currentValues) =>
    prevValues.job !== currentValues.job
  }
></Form.Item>
复制代码

但这又引起了另外一个问题,<Form.Item name="field" /> 只会对它的直接子元素绑定表单功能,就像这样:

<Form.Item label="Field" name="field">
  <Input />
</Form.Item>
复制代码

因此咱们须要经过添加 noStyle 将外层 Form.Item 做为纯粹的无样式绑定组件。

实现

<Form.Item
  noStyle
  shouldUpdate={(prevValues, currentValues) =>
    prevValues.job !== currentValues.job
  }
>
  {({ getFieldValue }) =>
    getFieldValue('job') !== 'student' ? (
      <Form.Item name="income" label="Income" rules={[{ required: true }]}> <Input /> </Form.Item>
    ) : null
  }
</Form.Item>
复制代码

动态表单完整代码

import { useState, useEffect } from 'react';
import { Form, Input, Select, Button } from 'antd';

const { Option } = Select;

const DynamicForm = () => {
  const [form] = Form.useForm();
  const [jobs, setJobs] = useState([]);

  const layout = {
    labelCol: { span: 6 },
    wrapperCol: { span: 12 },
  };

  useEffect(() => {
    const timer = setTimeout(() => {
      setJobs(['engineer', 'student', 'doctor']);
    }, 2000);
    return function clear() {
      clearTimeout(timer);
    };
  }, []);

  return (
    <Form form={form} {...layout}> <Form.Item name="name" label="User Name" rules={[{ required: true }]}> <Input /> </Form.Item> <Form.Item name="job" label="Job" rules={[{ required: true }]}> <Select placeholder="Select a option and change input text above" allowClear > {jobs.length > 0 ? ( jobs.map((job) => ( <Option key={job} value={job}> {job} </Option> )) ) : ( <Option>loading</Option> )} </Select> </Form.Item> <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.job !== currentValues.job } > {({ getFieldValue }) => getFieldValue('job') !== 'student' ? ( <Form.Item name="income" label="Income" rules={[{ required: true }]} > <Input /> </Form.Item> ) : null } </Form.Item> <Form.Item> <Button type="primary" htmlType="submit"> Submit </Button> </Form.Item> </Form>
  );
};

export default DynamicForm;
复制代码

React 最佳实践

相关文章
相关标签/搜索