表单项依赖处理方案

63 阅读4分钟

代码层面,分了三个模块,一、UI渲染层,二、配置层,三、依赖处理层;
不复杂不复杂,清理完成后,比我想象的还要简单

1、UI渲染层:

2、配置层:



3、依赖处理逻辑层:

1、收集 表单项的所有依赖关系dependencies;

2、由Form组件的onValuesChange,来触发依赖项的动态更新,并在onValuesChange中收集子级依赖更新项,再手动(setState)触发下一轮的onValuesChange;

import { createFormItemType } from '@/pages/tool/launch/Market/components/CreateForm';
import { FormInstance } from 'antd/es/form';
import { useEffect, useMemo, useState } from 'react';

export type HandlersType = {
  // 显示隐藏: 改变item的show属性值,
  show?: (value: any, formData?: any, item?: createFormItemType) => boolean;
  // 输入值:使用form.setFieldValue(key, newValue), 改变form[key]的值, 
  // 返回一个新的form[key]的值
  valueChange?: (value: any, formData: any, item: createFormItemType) => any;
  // 配置参数: 改变item的任意属性值, 该函数要返回一个新的item的属性的对象吗?还是直接覆盖item的属性值即可?
  // 是show的升级版,可以代替show方法
  propsChange?: (value: any, formData: any, item: createFormItemType) => any;
  // TODO: 还有啥?
  // TODO: 如果是异步方法怎么处理?
  [key: string]:
    | undefined
    | ((value: any, formData: any, item: createFormItemType) => any);
};

export type DependenciesDefineType = {
  reliedKey: string;
  handlers: HandlersType;
};

export type DependenciesMapType = {
  [reliedKey: string]: {
    [key: string]: HandlersType;
  };
};

interface Props {
  form: FormInstance<any>;
  createFormItems: { [key: string]: createFormItemType };
  initialValue?: any;
}

export const useFormItemsRelys = (props: Props) => {
  const { form, createFormItems, initialValue } = props;

  // 运行时依赖项reliedKey收集
  const [runningReliedKey, setRunningReliedKey] = useState<string[]>([]);
  // 触发主组件重新render
  const [, setRerender] = useState(0);

  // 收集依赖
  const { dependencies, formItems } = useMemo(() => {
    const dependencies: DependenciesMapType = {};
    // 初始化时 被依赖项
    const initialDependentKeysMap = new Set();
    const formItems = Object.entries(createFormItems).map(([key, config]) => {
      if (config.dependencies && Array.isArray(config.dependencies)) {
        config.dependencies.forEach(({ reliedKey, handlers }) => {
          if (!dependencies[reliedKey]) {
            dependencies[reliedKey] = {};
          }
          dependencies[reliedKey][key] = handlers as HandlersType;
          // 收集 初始化时 被依赖项
          initialDependentKeysMap.add(reliedKey);
        });
      }
      config.key = key;
      return config;
    });
    // 保存 初始化时 被依赖项
    setRunningReliedKey([...initialDependentKeysMap] as React.SetStateAction<
      string[]
    >);
    return {
      formItems,
      dependencies,
    };
  }, []);

  // 命中依赖,一次只能命中一层
const onValuesChange = (changeValue: any, formData: any) => {
  const newRunningReliedKey: React.SetStateAction<string[]> = [];
  Object.entries(dependencies).forEach(([reliedKey, handlers]) => {
    if (reliedKey in changeValue) {
      Object.entries(handlers).forEach(([key, handler]) => {
        /**
        * 目前这个命中的方法是写在代码里面的,没有抽出单独的命中逻辑这个命中逻辑
        * TODO:应该要抽一个单独的命中逻辑,并对handles做拓展
        * TODO:并且不兼容Form.Item的name属性的数组格式
        */ 
        // 每一个都是详细更新到当前的key对应的依赖项的, 不会更新到其他项的
        const { show, valueChange, propsChange } = handler;
        if (show || propsChange){
          let updateItemProps: createFormItemType = {};
          if (show) {
            const isShow = show(
              changeValue[reliedKey],
              formData,
              createFormItems[key],
            );
            updateItemProps['show'] = isShow;
          }
          if (propsChange) {
            updateItemProps = propsChange(
              changeValue[reliedKey],
              formData,
              createFormItems[key],
            );
          }
          Object.assign(createFormItems[key], updateItemProps);
        }
        if (valueChange) {
          const newFormValues = valueChange(
            changeValue[reliedKey],
            formData,
            createFormItems[key],
          );
          // 不会触发主组件更新
          form.setFieldValue(key, newFormValues);
        } 
        if (key in dependencies) {
          // 收集本次编辑,触发的子级被依赖字段
          newRunningReliedKey.push(key);
        } 
      });
      return;
    }
  });
  // 保存 子级被依赖字段,同时触发本次依赖命中的更新 触发渲染
  if (newRunningReliedKey && !!newRunningReliedKey.length) {
    setRunningReliedKey(newRunningReliedKey);
  } else {
    // 最后一层依赖的更新 触发渲染
    setRerender(Date.now());
  }
};

	// 触发子级更新,不做初始化的无效更新
  useEffect(() => {
    if (runningReliedKey && !!runningReliedKey.length) {
      const newFormData = form.getFieldsValue();
      const curChangeValue: any = {};
      runningReliedKey.forEach((reliedKey) => {
        curChangeValue[reliedKey] = newFormData[reliedKey];
      });
      onValuesChange(curChangeValue, newFormData);
    }
  }, [runningReliedKey]);

  return {
    formItems,
    onValuesChange,
  };
};

AI优化版:

Qwen:

import { createFormItemType } from '@/pages/tool/launch/Market/components/CreateForm';
import { FormInstance } from 'antd/es/form';
import { useEffect, useMemo, useState, useCallback, useRef } from 'react';

export type HandlersType = {
  show?: (value: any, formData?: any, item?: createFormItemType) => boolean;
  valueChange?: (value: any, formData: any, item: createFormItemType) => any;
  propsChange?: (value: any, formData: any, item: createFormItemType) => Partial<createFormItemType>;
  [key: string]: 
    | undefined 
    | ((value: any, formData: any, item: createFormItemType) => any);
};

export type DependenciesDefineType = {
  reliedKey: string;
  handlers: HandlersType;
};

export type DependenciesMapType = {
  [reliedKey: string]: {
    [key: string]: HandlersType;
  };
};

interface Props {
  form: FormInstance<any>;
  createFormItems: { [key: string]: createFormItemType };
  initialValue?: any;
}

export const useFormItemsRelys = (props: Props) => {
  const { form, createFormItems, initialValue } = props;
  
  // 使用ref保存最新配置,避免闭包问题
  const formItemsRef = useRef<{ [key: string]: createFormItemType }>(createFormItems);
  formItemsRef.current = createFormItems;

  // 收集依赖
  const { dependencies, formItems } = useMemo(() => {
    const dependencies: DependenciesMapType = {};
    const initialDependentKeysMap = new Set<string>();
    
    const processedFormItems = Object.entries(createFormItems).map(([key, config]) => {
      const newConfig = { ...config, key };
      
      if (Array.isArray(newConfig.dependencies)) {
        newConfig.dependencies.forEach(({ reliedKey, handlers }) => {
          if (!dependencies[reliedKey]) {
            dependencies[reliedKey] = {};
          }
          dependencies[reliedKey][key] = handlers;
          initialDependentKeysMap.add(reliedKey);
        });
      }
      
      return newConfig;
    });

    return {
      formItems: processedFormItems,
      dependencies
    };
  }, [createFormItems]);

  // 处理表单值变化
  const onValuesChange = useCallback((changedValues: any, allValues: any) => {
    const runningKeys = new Set<string>();
    
    // 遍历所有被依赖的字段
    Object.entries(dependencies).forEach(([reliedKey, handlers]) => {
      // 检查当前变化的字段是否匹配依赖项
      if (changedValues[reliedKey] !== undefined) {
        Object.entries(handlers).forEach(([targetKey, handler]) => {
          const targetItem = formItemsRef.current[targetKey];
          const currentFormData = form.getFieldsValue();
          
          if (targetItem) {
            const { show, valueChange, propsChange } = handler;
            const newValue = changedValues[reliedKey];
            
            // 处理显示逻辑
            if (show || propsChange) {
              let updateProps: Partial<createFormItemType> = {};
              
              if (show) {
                updateProps.show = show(newValue, currentFormData, targetItem);
              }
              
              if (propsChange) {
                updateProps = propsChange(newValue, currentFormData, targetItem);
              }
              
              // 更新表单项配置(需触发组件更新)
              formItemsRef.current[targetKey] = {
                ...formItemsRef.current[targetKey],
                ...updateProps
              };
            }
            
            // 处理值变化逻辑
            if (valueChange) {
              const newTargetValue = valueChange(newValue, currentFormData, targetItem);
              form.setFieldValue(targetKey, newTargetValue);
            }
            
            // 收集触发的下一级依赖
            if (targetKey in dependencies) {
              runningKeys.add(targetKey);
            }
          }
        });
      }
    });
    
    // 触发下一级依赖更新
    if (runningKeys.size > 0) {
      const nextChange = {};
      runningKeys.forEach(key => {
        nextChange[key] = form.getFieldValue(key);
      });
      
      form.setFieldsValue(nextChange);
    }
  }, [dependencies]);

  // 初始化时处理依赖
  useEffect(() => {
    if (initialValue) {
      form.setFieldsValue(initialValue);
    }
    
    // 监听表单变化
    const subscription = form.subscribe((changedValues, allValues) => {
      onValuesChange(changedValues, allValues);
    });
    
    return () => {
      subscription.unsubscribe();
    };
  }, [form, onValuesChange, initialValue]);

  return {
    formItems,
    onValuesChange
  };
};