useEffect cleanup每次渲染触发,如何实现组件卸载时仅表单变更提交保存
问题原因
你代码里的问题主要出在以下几点:
useEffect没有传入依赖数组,会导致每次组件渲染都会重新执行该effect,对应的清理函数也会在每次渲染前触发,而不是仅在组件卸载时执行- 误用了
React.memo(该API用于组件渲染优化,而非缓存计算值),应该替换为useMemo来计算表单是否变更 - 清理函数内判断逻辑错误:你判断的是
setIsFormChanged(状态更新函数,永远为真值),而非实际代表表单是否变更的isFormChanged状态 - 闭包陷阱:如果
useEffect的依赖为空数组,清理函数内直接读取isFormChanged会停留在初始值,需要用ref同步最新的表单变更状态,或者直接在清理函数内实时对比表单值
修正后的实现代码
import { useCallback, useEffect, useState, useMemo, useRef } from "react"; import React from "react"; import * as _ from "lodash"; import { useFormik } from "formik"; // 此处目前是硬编码,假设该服务端数据会在组件挂载时加载 const serverData = { choice: "yes", comment: "some existing comment" }; const availableChoices = ["yes", "no"]; const Form = () => { const formik = useFormik({ initialValues: { ...serverData } }); // 用ref存最新的表单变更状态,避免闭包问题 const isFormChangedRef = useRef(false); // 用useMemo缓存计算结果,仅当formik值变化时重新计算 const valuesHaveChanged = useMemo(() => { console.log("INIT VALUES= ", formik.initialValues); console.log("FINAL VALUES = ", formik.values); return !_.isEqual(formik.initialValues, formik.values); }, [formik.initialValues, formik.values]); // 同步变更状态到ref useEffect(() => { isFormChangedRef.current = valuesHaveChanged; }, [valuesHaveChanged]); const triggerSave = () => { console.log("Save", formik.values); // 这里写实际提交服务端的逻辑 }; // 仅在组件卸载时执行的effect,依赖为空数组 useEffect(() => { return () => { if (isFormChangedRef.current) { triggerSave(); } }; }, []); return ( <form> <div className="form-group"> {availableChoices.map((choice) => ( <label key={choice}> {choice} <input id="choice" value={choice} className="form-control" type="radio" name="choice" checked={choice === formik.values.choice} onChange={formik.handleChange} /> </label> ))} </div> <div className="form-group"> <textarea rows="5" cols="30" id="comment" name="comment" value={formik.values.comment} onChange={formik.handleChange} className="form-control" placeholder="some text..." ></textarea> </div> </form> ); }; export default Form;
核心改动说明
- 给卸载时执行的
useEffect传入空依赖数组,保证仅在组件卸载时触发一次清理函数 - 用
useMemo替换React.memo计算表单变更状态,避免无效重复计算 - 新增
useRef存储最新的表单变更状态,绕过闭包陷阱,保证卸载时能拿到最新的状态判断结果 - 修正清理函数的判断逻辑,仅当表单真的发生变更时才触发保存操作
内容的提问来源于stack exchange,提问作者Pratik Wadekar
相关产品推荐
相关产品推荐

