You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 15:54:03