基於 Ant Design
, 咱們來實現一個【薪資調查表】,需求有二:html
職位(Job)列表
模擬從服務端拉取 react
當職位是 Student
時,薪資(Income)
輸入框消失。 web
咱們先用 Ant Design 提供的 Form, Input, Select, Button
組件來構建 UI 架構。數組
labelCol
爲 label
標籤佈局,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
const [state, setState] = useState(initialState);
複製代碼
它會返回一個 state
,以及更新 state
的函數。框架
initialState
會做爲 state
的初始值。函數
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;
複製代碼