返回
解决React useWatch返回undefined的终极指南
javascript
2024-12-18 08:15:46
useWatch 返回 undefined 的问题排查与解决
当使用 react-hook-form
库的 useWatch
钩子时,即使已经通过 defaultValues
设置了默认值,仍然可能遇到 useWatch
返回 undefined
的情况。本文将深入探讨此问题的原因,并提供多种解决方案。
问题分析
useWatch
返回 undefined
通常是由于以下原因造成的:
- 异步数据加载: 如果
defaultValues
中包含异步加载的数据,在数据加载完成之前,useWatch
可能会获取到undefined
。 - 嵌套字段问题: 当处理嵌套对象结构时,如果没有正确地初始化所有嵌套字段,
useWatch
可能无法监听到深层字段的变化。 control
对象未正确传递:useWatch
需要接收react-hook-form
的control
对象才能正常工作。如果没有正确传递control
对象,会导致useWatch
无法获取表单状态。
解决方案
针对上述问题,可以采用以下几种解决方案:
-
确保异步数据加载完成后再使用
useWatch
如果
defaultValues
依赖于异步数据,需要确保数据加载完成后再进行渲染或使用useWatch
。 可以通过以下方法实现:-
条件渲染: 使用一个状态变量来跟踪数据是否加载完成,并在数据加载完成后再渲染依赖于
useWatch
的组件。代码示例:
import React, { useState, useEffect } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { vcardContentSchema, FormData } from './schema'; // 假设 schema 已定义 const MyComponent = ({ vcard }) => { const [isLoading, setIsLoading] = useState(true); const [initialValues, setInitialValues] = useState(null); useEffect(() => { const fetchData = async () => { // 模拟异步加载数据 await new Promise((resolve) => setTimeout(resolve, 500)); setInitialValues({ basicInfo: { title: vcard.content.basicInfo.title || '', heading: vcard.content.basicInfo.heading || '', subheading: vcard.content.basicInfo.subheading || '', avatar: vcard.content.basicInfo.avatar || '', }, }); setIsLoading(false); }; fetchData(); }, [vcard]); const { control, getValues } = useForm<FormData>({ resolver: zodResolver(vcardContentSchema), defaultValues: initialValues || {}, // 初始值为 null 或加载的数据 }); const formValues = useWatch({ control, defaultValue: getValues(), }); if (isLoading) { return <p>Loading...</p>; } return ( <div> {/* 使用 formValues */} <pre>{JSON.stringify(formValues, null, 2)}</pre> </div> );
};
export default MyComponent; ``` **操作步骤:** 1. 引入 `useState` 和 `useEffect` 钩子。 2. 定义一个 `isLoading` 状态变量,初始值为 `true`。 3. 定义 `initialValues` 状态变量来存储加载后的数据, 初始化为null。 4. 使用 `useEffect` 模拟异步数据加载,并在加载完成后将 `isLoading` 设置为 `false` 以及 `initialValues`设置为加载好的数据。 5. 在 `isLoading` 为 `true` 时,显示加载提示。 6. 当 `isLoading` 为 `false` 时,渲染依赖于 `useWatch` 的组件,此时 `formValues` 应包含正确的默认值。
- 使用
useEffect
监听数据变化: 使用useEffect
监听defaultValues
依赖的数据的变化,当数据加载完成后,再重新设置defaultValues
。
-
-
初始化所有嵌套字段
为了避免
useWatch
在处理嵌套字段时返回undefined
,需要确保所有嵌套字段都已正确初始化。可以采用以下方法:-
提供完整的默认值结构: 在
defaultValues
中提供完整的嵌套对象结构,即使某些字段为空值。代码示例:
const { register, handleSubmit, setValue, getValues, control, formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(vcardContentSchema), defaultValues: { basicInfo: { title: vcard.content.basicInfo.title || '', heading: vcard.content.basicInfo.heading || '', subheading: vcard.content.basicInfo.subheading || '', avatar: vcard.content.basicInfo.avatar || '', }, // 确保其他顶层字段也被初始化,即使为空 otherField1: '', otherField2: null, }, }); const formValues = useWatch({ control, defaultValue: getValues(), });
操作步骤:
- 修改
defaultValues
,确保包含FormData
接口中定义的所有顶层字段。 - 为每个字段提供合适的默认值,例如空字符串、
null
或其他合适的值。
- 修改
-
使用 Lodash 的
_.defaultsDeep
进行深度默认值合并: Lodash 库的_.defaultsDeep
方法可以递归地合并默认值,确保所有嵌套字段都被初始化。代码示例:
import _ from 'lodash'; import { useForm, useWatch } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { vcardContentSchema, FormData } from './schema'; // 假设 schema 已定义 const MyComponent = ({ vcard }) => { const defaultValues = { basicInfo: { title: vcard.content.basicInfo.title, heading: vcard.content.basicInfo.heading, subheading: vcard.content.basicInfo.subheading, avatar: vcard.content.basicInfo.avatar, }, otherField1: '', otherField2: null }; // 使用 lodash 的 defaultsDeep 方法进行深度合并,处理可能的 undefined 值 const resolvedDefaultValues = _.defaultsDeep({}, defaultValues, { basicInfo: { title: '', heading: '', subheading: '', avatar: '' }, otherField1: '', otherField2: null }); const { control, getValues } = useForm<FormData>({ resolver: zodResolver(vcardContentSchema), defaultValues: resolvedDefaultValues, }); const formValues = useWatch({ control, defaultValue: getValues(), }); return ( <div> <pre>{JSON.stringify(formValues, null, 2)}</pre> </div> ); }; export default MyComponent;
操作步骤:
- 安装 Lodash:
npm install lodash
或yarn add lodash
。 - 引入
lodash
库。 - 使用
_.defaultsDeep
方法合并默认值,确保所有嵌套字段都有默认值。
- 安装 Lodash:
-
-
检查
control
对象的传递确保将
useForm
返回的control
对象正确传递给useWatch
。 仔细检查代码,避免拼写错误或其他导致control
对象未正确传递的错误。代码示例:
const { register, handleSubmit, setValue, getValues, control, // 从 useForm 获取 control 对象 formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(vcardContentSchema), defaultValues: { basicInfo: { title: vcard.content.basicInfo.title || '', heading: vcard.content.basicInfo.heading || '', subheading: vcard.content.basicInfo.subheading || '', avatar: vcard.content.basicInfo.avatar || '', }, } }); const formValues = useWatch({ control, // 将 control 对象传递给 useWatch defaultValue: getValues(), });
操作步骤:
- 确保从
useForm
钩子函数中正确解构出control
对象。 - 将
control
对象传递给useWatch
钩子函数的control
属性。
- 确保从