当React组件传入的props变化时,持久化Formik表单输入值
解决Formik子组件因props更新丢失输入内容的问题
核心问题原因
你遇到的问题本质是:当直接将父组件传递的props作为Formik的initialValues时,每次props更新都会触发Formik重置初始值,导致用户已输入的内容被覆盖。同时父组件其他事件触发的重渲染,如果子组件没有做优化,也会跟着重渲染,加重这个问题。
具体解决方案
1. 分离初始值与动态props,仅首次渲染设置初始值
只在组件第一次挂载时用props初始化表单,后续props变化不再重置已输入内容。可以用useRef标记首次渲染,配合useEffect控制逻辑:
import { useFormik } from 'formik'; import { useEffect, useRef } from 'react'; const ChildForm = ({ options }) => { const isFirstRender = useRef(true); const formik = useFormik({ initialValues: { selectedOption: options[0]?.value || '' }, onSubmit: (values) => { console.log('提交内容:', values); }, }); useEffect(() => { // 仅首次渲染后完成初始化标记,后续props变化不重置表单 if (isFirstRender.current) { isFirstRender.current = false; return; } // 若需要根据新props更新可选选项(而非重置输入),可在这里处理下拉框选项更新 }, [options]); return ( <form onSubmit={formik.handleSubmit}> <select name="selectedOption" value={formik.values.selectedOption} onChange={formik.handleChange} > {options.map(opt => ( <option key={opt.value} value={opt.value}>{opt.label}</option> ))} </select> <button type="submit">提交</button> </form> ); };
2. 谨慎使用enableReinitialize属性
如果确实需要在特定场景下重置表单,可使用Formik的enableReinitialize,但不要直接设为true,而是通过一个控制变量决定是否重置:
const ChildForm = ({ options, shouldResetForm }) => { const formik = useFormik({ initialValues: { selectedOption: options[0]?.value || '' }, enableReinitialize: shouldResetForm, // 仅当需要重置时开启 onSubmit: (values) => { console.log('提交内容:', values); }, }); // 表单结构同上 };
父组件中仅在需要重置表单的事件触发时,将shouldResetForm设为true,完成后再设回false。
3. 优化子组件渲染,避免不必要重渲染
用React.memo包裹子组件,同时父组件中用useMemo缓存引用类型的props(比如options数组),确保只有当props真正变化时子组件才重渲染:
// 父组件 import { useState, useMemo } from 'react'; import ChildForm from './ChildForm'; const ParentComponent = () => { const [otherState, setOtherState] = useState(false); // 用useMemo缓存options,避免父组件其他状态变化时生成新的数组引用 const options = useMemo(() => [ { value: '1', label: '选项1' }, { value: '2', label: '选项2' }, ], []); return ( <div> <button onClick={() => setOtherState(!otherState)}>触发其他事件</button> <ChildForm options={options} /> </div> ); }; // 子组件 import { useFormik } from 'formik'; import React from 'react'; const ChildForm = React.memo(({ options }) => { const formik = useFormik({ initialValues: { selectedOption: options[0]?.value || '' }, onSubmit: (values) => { console.log('提交内容:', values); }, }); return ( <form onSubmit={formik.handleSubmit}> <select name="selectedOption" value={formik.values.selectedOption} onChange={formik.handleChange} > {options.map(opt => ( <option key={opt.value} value={opt.value}>{opt.label}</option> ))} </select> <button type="submit">提交</button> </form> ); });
内容的提问来源于stack exchange,提问作者Karim SOUARI
相关产品推荐
相关产品推荐

