【问题标题】:How to fix update on unmounted component?如何修复未安装组件的更新?
【发布时间】:2021-02-04 06:16:34
【问题描述】:

您好,我有一个灯箱功能,但是当我打开灯箱时,我收到一条警告,说我无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。谁能帮我解决这个问题?

export function ImageLightBox({ image }) {
  const KEY_ESC = 27
  const [visible, setVisible] = React.useState(false)
  return (
    <div className={cx(styles.component )} aria-modal='true'>
      <Lightbox {...{ visible, image }} onClose={() => setVisible(false)} />
      <div className={styles.content}>
        <img className={styles.image} src={image.src} width={image.width} alt={image.alt} height={image.height} />
        <div className={styles.buttonOpen}><LinkSecondary onClick={OpenLightbox} iconLeft={iconZoom}>Vergroot</LinkSecondary></div>
      </div>
    </div>
  )

  function OpenLightbox() {
    window.addEventListener('keydown', handleKeyDown)
    setVisible(true)
    function handleKeyDown(e) {
      if (e.which === KEY_ESC) setVisible(false)
    }
  }
}

function Lightbox({ visible, onClose, image }) {
  const animatedBackground = useTransition(!visible, null, {
    from: { opacity: 0, position: 'absolute' },
    enter: { opacity: 1, position: 'absolute' },
    leave: { opacity: 0, position: 'absolute' },
    config: config.gentle,
  })
  const portalNode = usePortalNode(visible ? '_rootImageWithLightbox' : '_rootImageWithLightbox-default')
  return portalNode && ReactDOM.createPortal(
    animatedBackground.map(({ props, key }) => (
      visible && <animated.div style={props} key={key} className={styles.componentLightbox}>
        <div className={styles.buttonClose}><LinkPrimary hideText onClick={onClose} iconRight={closeSvg}>Sluiten</LinkPrimary></div>
        <img className={styles.imageLightbox} srcSet={image.lightboxSrcSet} src={image.lightboxSrc} alt={image.alt} />
        <div onClick={onClose} className={styles.backdrop} />
      </animated.div>
    ))
    , portalNode
  )
}

【问题讨论】:

  • 您需要移除 keydown 事件监听器,否则它将保持活动状态,导致钩子永远不会被 GC。

标签: reactjs


【解决方案1】:

任何订阅都是一个好主意;所以在组件卸载时移除 keydown 事件监听器;

  useEffect(() => {
    return () => {
        window.removeEventListener('keydown')
    }
  }, [])

我也怀疑这条onClose={() =&gt; setVisible(false)} 行;因为setState 是异步的,父组件可能已卸载,但onClose 回调将在门户(子)组件中运行,因此会发出警告。

【讨论】:

  • 不,除此之外,我仍然收到该警告。顺便说一句,我放置useEffect 的地方位于我拥有 React.useState 的地方下方。那是正确的地方吗?因为在你放它的地方,是无法到达的。
【解决方案2】:

尝试在组件挂载时添加监听器(componentDidMountuseEffect)。

您的代码中的问题是您在函数中添加了侦听器,并且每次调用它时,它总是开始侦听。

我会添加到@Leonardo 的答案

useEffect(()=>{
  const handleKeydown = ()=>{
  // your implementations here
  // and yes, you define the handleKeydown here inside useEffect
  }

  window.addListener('keydown', handleKeydown)

  // this peace of code will be run when your component unmounts
  return window.removeListener('keydown');

  // this [] means it only run once on mount.
},[])

【讨论】:

  • 但是我必须使用 useEffect 的地方是什么?
  • 直接在你的组件中,不嵌套在任何函数中。我个人的偏好是在我使用useStates 声明之后
猜你喜欢
  • 2021-11-30
  • 1970-01-01
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 1970-01-01
  • 2022-01-07
  • 1970-01-01
相关资源
最近更新 更多