如何使用useCallback结合Formik的innerRef监听状态变化,实现外部提交与按钮禁用逻辑
解决Formik外部调用提交与监听isValid状态的问题
我刚好有过类似的实践经验,咱们一步步来解决你的两个核心需求:
需求1:从Formik外部调用handleSubmit
你当前用useRef绑定innerRef的方式其实是可行的,但问题在于ref的current变化不会触发组件重渲染或useEffect,所以没法监听isValid状态。不过有更优雅的方式来同时满足两个需求:
方案一:改用useFormik Hook(推荐)
直接使用Formik的useFormik Hook可以让你直接拿到响应式的formik实例,不需要借助ref就能访问handleSubmit和isValid,代码更简洁:
const MyModal = () => { // 直接初始化Formik实例 const formik = useFormik({ ...formikProps, // 传入你的初始配置(initialValues、validationSchema等) }); // 外部触发提交的函数 const doSave = () => { formik.handleSubmit(); }; return ( <Modal> <ModalHeader>My Modal Header</ModalHeader> <ModalBody> {/* 直接使用formik实例的属性渲染表单 */} <form onSubmit={formik.handleSubmit}> {/* 示例表单控件 */} <input name="username" value={formik.values.username} onChange={formik.handleChange} onBlur={formik.handleBlur} /> {formik.touched.username && formik.errors.username && ( <div className="error">{formik.errors.username}</div> )} {/* 其他表单字段 */} </form> </ModalBody> <ModalFooter> {/* 直接用formik.isValid控制按钮禁用,无需额外状态 */} <Button disabled={!formik.isValid} onClick={doSave}> Save </Button> </ModalFooter> </Modal> ); };
这种方式的好处是:
- 无需维护额外的
isSavingDisabled状态,直接用!formik.isValid控制按钮禁用 formik实例的属性是响应式的,isValid变化会自动触发组件重渲染,按钮状态实时更新- 直接调用
formik.handleSubmit即可完成外部提交,不用依赖ref
方案二:保留<Formik>组件式写法
如果你更习惯用组件式的Formik,可以通过子组件函数的props获取isValid,并监听其变化来更新按钮状态:
const MyModal = () => { const [isSavingDisabled, setSavingDisabled] = useState(false); const formikRef = useRef(); const doSave = () => { formikRef.current?.handleSubmit(); }; return ( <Modal> <ModalHeader>My Modal Header</ModalHeader> <ModalBody> <Formik innerRef={formikRef} {...formikProps}> {(props) => { // 监听isValid变化,更新按钮禁用状态 useEffect(() => { setSavingDisabled(!props.isValid); }, [props.isValid]); return ( <> {/* 你的表单内容,使用props中的formik属性 */} <Field name="email" /> {props.touched.email && props.errors.email && ( <div>{props.errors.email}</div> )} </> ); }} </Formik> </ModalBody> <ModalFooter> <Button disabled={isSavingDisabled} onClick={doSave}> Save </Button> </ModalFooter> </Modal> ); };
为什么你之前的useCallback尝试会导致渲染循环?
你提到用useCallback配合useState出现循环,大概率是因为你把更新状态的逻辑放在了useCallback的依赖里,导致状态更新后重新创建回调,回调又触发状态更新,形成循环。上面的两种方案都避开了这个问题:
- 方案一直接依赖响应式的formik属性,无需额外状态
- 方案二在Formik的子函数里用
useEffect监听isValid,只会在isValid变化时触发更新
内容的提问来源于stack exchange,提问作者diekunstderfuge
相关产品推荐
相关产品推荐

