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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 23:15:00