likes
comments
collection
share

封装两个表单 hook -- useFetchOptions 和 useUpdateFormFields

作者站长头像
站长
· 阅读数 28

由于本人最近写了大量的表单,深感表单功能实现过程中的烦恼和无趣。因此,在众多繁杂的业务代码中抽象出来两个 hook 以便后续继续写表单。本文中将介绍这两个 React Hooks :useFetchOptionsuseUpdateFormFields。前者用来向后端请求下拉列表的 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的useStateuseEffect 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
评论
请登录