antd的Proform/Form组件setFieldValue不会触发render

615 阅读2分钟

问题

我们在使用form组件的时候,如果需要通过form?.getFieldValue的值来,控制某个组件的渲染

例如:

    //reat组件
    return form?.getFieldValue('name') === '张三' && <>一种展示样式</>

我们印象中使用 form?.getFieldValue('name') 来进行判断,当name='张三'的时候会展示后面的组件。但是这样写并不会触发render的重新渲染,不会更新组件

方案1:

通常有的同学会联想到使用 useState,创建一个变量来记录name的改变的值,让useState触发重新渲染。但是如果我们需要使用大量的form的表单项的值来控制组件的展示与否,我们会定义大量的useState变量。

方案2:

使用useWatch —监听表单字段,但是这样也会创建大量的useWatch来监听。

const [useform] = Form.useForm();
const nameValue = Form.useWatch(['name'], useform); // 监听对象可以是数组,字符串,数字
console.log("nameValue",nameValue); 每次更新,这里都重新渲染

**组件***
   <ProForm      
       form={useform}
        ....
     >
     <ProFormText
       label={'用户名称'}
       name={'name'} // 监听对象
       placeholder={'请输入用户名称'}
       rules={[{required: true}]}
      />
     </ProForm>

这样的写法看来并不是很优雅。

如何优雅的解决呢?

我们可以通过自定义hooks,然后重写form?.setFieldValue()函数来解决我们创建大量的useState的问题.

自定义hooks

import {useEffect, useState} from "react";
import {FormInstance} from "antd";
/*
    针对:form.setFieldValue({name:value})不会触发render,无法使用useEffect监听,不能实现数据双向绑定的效果。
    解决问题:不用再注册过多useState来进行数据的监听。
    思路: 重写setFieldsValue方法,利用useState进行数据记录
* */
export default function (form: FormInstance<any>) {
    const [formField,setFormField] = useState<FormInstance<any> | null>(null)
    useEffect(() => {
        setFormField(form)
        return () => {
            setFormField(null)
        };
    },[form])
    const setUseFormFieldValue = (data) => {
            _.keys(data)?.forEach((key) => {
                form.setFieldsValue({ [key]: data[key] });
            })
            // 触发组件重新渲染
            setFormField({ ...form });
    };
    return {
        useFormField: formField ? {...formField, setFieldsValue: setUseFormFieldValue} : null,
    }
}

使用

我这里用的antd的pro组件


import {UseFormFieldsValue} from "@/hooks"; // 我自己定一个hooks名称,你们可以定义成别的
const [useform] = Form.useForm();
const { useFormField } = UseFormFieldsValue(useform);

  <ProForm.Item
	name={'name'}
     	label={'名字'}
        rules={[{required: true}]}
      >
                                                            
       <Input onChange={(value)=>{useFormField?.setFieldsValue({name: value})}}>
     </ProForm.Item>