封装两个表单 hook -- useFetchOptions 和 useUpdateFormFields
由于本人最近写了大量的表单,深感表单功能实现过程中的烦恼和无趣。因此,在众多繁杂的业务代码中抽象出来两个 hook 以便后续继续写表单。本文中将介绍这两个 React Hooks :useFetchOptions
和useUpdateFormFields
。前者用来向后端请求下拉列表的 items, 后者用于表单数据的回填。
1. 功能概述
useFetchOptions
是一个自定义Hook,用于从指定URL获取数据,并将其转换为选项数组。它提供了灵活的HTTP请求方法和数据转换功能,适用于动态加载下拉菜单、选择框等组件的选项数据。
useUpdateFormFields
是另一个自定义Hook,它的作用是自动更新表单字段的值。通过传入表单引用、信息对象和字段转换规则,该Hook可以实现在组件状态更新时自动将新的数据设置到表单中,并根据需要转换字段名和值。
2. 接口说明
useFetchOptions
接收以下参数:
url
: 请求的URL。method
: HTTP请求方法,默认为'GET'。labelProp
: 用于从响应数据中提取标签的属性名。valueProp
: 用于从响应数据中提取值的属性名。transformResponse
: 转换响应数据的函数,默认为提取响应中的records
字段。configure
: 请求的其他配置信息。
useUpdateFormFields
接收以下参数:
formRef
: 指向当前表单实例的React ref对象。info
: 包含需要设置到表单中的数据的对象。transformer
: 定义字段转换规则的对象,默认为空。transformer 不为空的时候其 key 对应 info中的某个 key ,value 是一个数组,数组的第一个元素是 key 的新名称,第二个元素是新旧属性值之间的转换函数。
3. 实现原理
useFetchOptions
通过使用React的useState
和useEffect
Hooks来管理状态和执行副作用。在URL或请求方法变化时,它会发送网络请求,并使用extendRequest
工具函数来获取数据。响应数据通过transformResponse
函数进行转换,并最终设置为选项数组的状态。同时,加载状态和错误信息也被相应地管理。
useUpdateFormFields
则依赖于useEffect
来监听表单引用和信息对象的变化。当这些依赖项变化时,它会创建一个信息对象的浅拷贝,并根据转换规则对字段进行转换。最后,使用表单实例的setFieldsValue
方法来更新表单字段的值。
4. 完整代码
4.1 useFetchOptions
代码如下:
// 引入React的useState和useEffect Hooks
import { useState, useEffect } from 'react';
// 引入一个名为extendRequest的工具函数,它可能用于发送网络请求
import { extendRequest } from '@/pages/utils';
// 定义一个自定义Hook函数useFetchOptions,用于从指定URL获取数据并转换为选项数组
// 参数包括:
// url: 请求的URL
// method: HTTP请求方法,默认为'GET'
// labelProp: 用于从响应数据中提取标签的属性名
// valueProp: 用于从响应数据中提取值的属性名
// transformResponse: 一个函数,用于转换响应数据,默认为提取响应数据中的records字段
// configure: 配置对象,可能包含请求的其他配置信息
function useFetchOptions(
url,
method = 'GET',
labelProp,
valueProp,
transformResponse = (response) => response.data?.records || [],
configure,
) {
// 使用useState Hook定义三个状态:options(选项数组)、loading(加载状态)、error(错误信息)
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 如果configure未定义,且请求方法为'POST',则为其设置一个默认对象
configure ??= method==='POST' ? {
data: { body: {}, query: {} },
}: {};
// 使用useEffect Hook,在url或method变化时发送网络请求
useEffect(() => {
setLoading(true); // 设置加载状态为true
setError(null); // 清除错误信息
// 使用extendRequest函数发送网络请求,并根据请求方法选择对应的函数
extendRequest[method.toLowerCase()](url, configure)
.then(response => {
// 转换响应数据,并映射为选项数组
const optionsList = transformResponse(response);
setOptions(optionsList.map(record => ({
value: record[valueProp], // 提取值属性
label: record[labelProp] // 提取标签属性
})));
})
.catch(error => {
setError(error); // 设置错误信息
})
.finally(() => {
setLoading(false); // 设置加载状态为false
});
}, [url, method]); // 依赖项为url和method
// 返回选项数组、加载状态和错误信息
return [options, loading, error];
}
// 导出自定义Hook函数useFetchOptions
export {useFetchOptions};
4.2 useUpdateFormFields
代码如下
// 引入React的useEffect Hook
import { useEffect } from 'react';
// 定义一个自定义Hook函数useUpdateFormFields,它接收三个参数:
// formRef: 一个React ref对象,指向当前的表单实例
// info: 一个包含需要设置到表单中的数据的对象
// transformer: 一个对象,用于定义需要转换的字段名和转换函数,默认为空对象
function useUpdateFormFields(formRef, info, transformer={}) {
// 使用useEffect Hook,在formRef或info变化时触发回调函数
useEffect(() => {
// 创建一个info的浅拷贝,避免直接修改原对象
const _ = {...info};
// 获取transformer对象的所有键(即需要转换的字段名)
const keys = Object.keys(transformer);
// 遍历所有需要转换的字段
keys.forEach(key => {
// 从transformer中获取新的字段名和转换函数,如果没有提供则使用默认值
const [newName='', transFun=()=>{}] = transformer[key];
// 如果新的字段名存在
if(newName){
// 使用转换函数将原字段的值转换为新值,并设置到新字段上
_[newName] = transFun(_[key]);
}
// 删除原字段
delete _[key];
})
// 使用表单实例的setFieldsValue方法设置表单字段的值
formRef.current?.setFieldsValue(_);
// 依赖项列表,当formRef或info变化时,useEffect的回调函数会被触发
}, [formRef, info]);
}
// 导出自定义Hook
export {useUpdateFormFields};
5. 使用举例
使用useFetchOptions
获取下拉选项数据:
import { useFetchOptions } from './hooks';
function DropdownComponent() {
const [options, loading, error] = useFetchOptions('https://api.example.com/options', 'GET', 'name', 'id');
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error fetching options: {error.message}</div>;
}
return (
<select>
{options.map(option => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
);
}
使用useUpdateFormFields
自动更新表单字段:
import { useRef, useState } from 'react';
import { Form, Input } from 'antd';
import { useUpdateFormFields } from './hooks';
function FormComponent() {
const formRef = useRef(null);
const [info, setInfo] = useState({});
useUpdateFormFields(formRef, info, { userName: ['username', value => value.toUpperCase()] });
const onFinish = values => {
console.log('Form values:', values);
};
return (
<Form ref={formRef} onFinish={onFinish}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input placeholder="Username" />
</Form.Item>
{/* Other form items */}
<Form.Item>
<button type="submit">Submit</button>
</Form.Item>
</Form>
);
}
在这个例子中,当info
状态更新时,useUpdateFormFields
会自动将新的userName
字段值转换为大写,并设置到表单的username
字段中。
转载自:https://juejin.cn/post/7361008454323175424