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

React useEffect卸载时意外引用最新props值问题

问题解决:React useEffect清理函数捕获错误的props值

问题根源

你遇到的是典型的React闭包陷阱,结合可能的引用类型变量特性导致:

  • useEffect依赖数组为空,effect仅在组件挂载时执行一次,对应的清理函数会捕获挂载时的变量。
  • 若accessor是数组、对象这类引用类型,即使挂载时捕获了它的引用,后续该引用的内容被修改时,清理函数打印或使用的会是修改后的最新内容。
  • 当组件因accessor或field变化重新渲染时,旧的清理函数不会更新,仍然引用最初的变量(或引用类型的最新内容),导致卸载时行为不符合预期。

解决方案

方案1:添加正确的依赖项(推荐)

将accessor、field和dispatch加入useEffect的依赖数组,这样每当这些值变化时,React会先执行上一次effect的清理函数,再运行新的effect,确保卸载时执行的清理函数对应组件最后一次渲染的props值:

const Group = ({
  children,
  accessor,
  disabled = false,
}: {
  children: React.ReactNode
  accessor: Accessor
  disabled?: boolean
}) => {
  const dispatch = useEditorDispatch()

  React.useEffect(() => {
    if (accessor.length === 1 && field === 'audienceExtension') return
    const attribute = `-${field}`
    console.log(`on mount ${accessor}`)
    dispatch({
      type: 'setError',
      payload: {
        message: 'is empty',
        attribute,
        accessor,
      },
    })

    return () => {
      console.log(`on unmount ${accessor}`)
      dispatch({
        type: 'setError',
        payload: { message: null, attribute, accessor },
      })
    }
  }, [accessor, field, dispatch]) // 补充依赖项
}

方案2:用useRef保存挂载时的快照

如果需要严格使用组件挂载时的accessor值(而非后续更新后的),可以用useRef保存挂载时的变量快照。若accessor是引用类型,记得做拷贝避免后续内容修改影响快照:

const Group = ({
  children,
  accessor,
  disabled = false,
}: {
  children: React.ReactNode
  accessor: Accessor
  disabled?: boolean
}) => {
  const dispatch = useEditorDispatch()
  // 保存挂载时的accessor快照(引用类型需拷贝)
  const savedAccessorRef = React.useRef<Accessor>(Array.isArray(accessor) ? [...accessor] : accessor)
  const savedAttributeRef = React.useRef<string>(`-${field}`)

  React.useEffect(() => {
    if (savedAccessorRef.current.length === 1 && field === 'audienceExtension') return
    console.log(`on mount ${savedAccessorRef.current}`)
    dispatch({
      type: 'setError',
      payload: {
        message: 'is empty',
        attribute: savedAttributeRef.current,
        accessor: savedAccessorRef.current,
      },
    })

    return () => {
      console.log(`on unmount ${savedAccessorRef.current}`)
      dispatch({
        type: 'setError',
        payload: { 
          message: null, 
          attribute: savedAttributeRef.current, 
          accessor: savedAccessorRef.current 
        },
      })
    }
  }, []) // 保持依赖数组为空
}

内容的提问来源于stack exchange,提问作者Antonio Santoro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:05:20