如何实现react-hook-form页面重载后保留用户已填写的表单值
解决方案
你完全不需要访问react-hook-form内部私有属性,官方对外暴露了合法的全量表单值获取和变化监听API,配合防抖逻辑即可实现低侵入的表单持久化,具体实现步骤如下:
步骤1:初始化时从本地存储读取数据回填
useForm的defaultValues参数支持传入初始化值,直接读取localStorage中已保存的内容传入即可实现页面刷新后自动回填:
const FORM_STORAGE_KEY = 'contract_form_data' // 读取本地存储的表单值 const getSavedFormData = () => { const savedData = localStorage.getItem(FORM_STORAGE_KEY) return savedData ? JSON.parse(savedData) : {} } const methods = useForm({ mode: 'onTouched', defaultValues: getSavedFormData() // 初始化直接回填已保存内容 }) const { handleSubmit, watch, getValues } = methods
步骤2:监听表单变化自动持久化
使用watch API监听所有表单字段的变化,配合防抖逻辑避免频繁写入localStorage:
如果项目没有引入lodash,也可以自己手写简单的防抖函数,核心是避免每次输入都触发存储操作
import { useEffect, useCallback } from 'react' import debounce from 'lodash/debounce' // 防抖存储逻辑,500ms无输入再写入本地存储 const saveToStorage = useCallback( debounce(() => { // 合法获取全量表单值,无需访问内部属性 const formValues = getValues() // 注意:你当前的三个生日选择组件是自行维护state的,没有注册到react-hook-form中,需要手动合并这三个值 const allValues = { ...formValues, birthYear: selectedYear, birthMonth: selectedMonth, birthDay: selectedDay } localStorage.setItem(FORM_STORAGE_KEY, JSON.stringify(allValues)) }, 500), [getValues, selectedYear, selectedMonth, selectedDay] ) useEffect(() => { // 订阅所有表单值的变化 const unsubscribe = watch(() => { saveToStorage() }) // 组件卸载时清理订阅和防抖任务 return () => { unsubscribe() saveToStorage.cancel() } }, [watch, saveToStorage])
步骤3:提交成功后清理本地存储
表单提交成功后删除本地存储的内容,避免后续进入表单残留旧数据:
const onSubmit = (formData) => { // 原有表单提交逻辑... // 提交成功后清理存储 localStorage.removeItem(FORM_STORAGE_KEY) }
高复用性优化
可以把上述逻辑抽成通用自定义Hook,所有需要持久化的表单都可以直接复用,完全符合DRY原则:
// 通用表单持久化Hook const usePersistForm = (storageKey, formConfig = {}) => { const defaultValues = useMemo(() => { const saved = localStorage.getItem(storageKey) return saved ? JSON.parse(saved) : formConfig.defaultValues || {} }, [storageKey, formConfig.defaultValues]) const methods = useForm({ ...formConfig, defaultValues }) const { watch, getValues } = methods const debounceTime = formConfig.debounce || 500 const save = useCallback( debounce(() => { localStorage.setItem(storageKey, JSON.stringify(getValues())) }, debounceTime), [getValues, storageKey, debounceTime] ) useEffect(() => { const unSub = watch(() => save()) return () => { unSub() save.cancel() } }, [watch, save]) const clearStorage = useCallback(() => { localStorage.removeItem(storageKey) }, [storageKey]) return { ...methods, clearStorage } } // 使用示例 const { handleSubmit, clearStorage } = usePersistForm('contract_form_data', { mode: 'onTouched', debounce: 300 })
补充说明:建议你把三个生日选择组件改用
useController注册到react-hook-form的管控中,之后就不需要手动合并生日相关的值,Hook会自动包含所有表单字段。
内容的提问来源于stack exchange,提问作者Katharina Schreiber
相关产品推荐
相关产品推荐

