反应状态未在嵌套事件处理程序中更新

问题描述 投票:0回答:1

请考虑以下代码:

import { useState, useEffect, useRef } from 'react';

const OtherComponent = ({ locked }) => {
  return (
    <div style={{position: 'fixed'}}>
      {locked ? 'Locked' : 'Unlocked'}
    </div>
  );
}

const PageComponent = () => {
  const [locked, setLocked] = useState(false);
  const mainRef = useRef(null);

  useEffect(() => {
    document.addEventListener('scroll', scrollListener, {passive: true});
  }, [])

  const scrollListener = () => {
    document.removeEventListener('scroll', scrollListener);

    setLocked(true);

    mainRef.current.addEventListener('wheel', wheelEventListener, {passive: true});
  }

  const wheelEventListener = () => {
    // locked always === false here, expecting true
    console.log('locked: ', locked);
  }

  return (
    <div ref={mainRef} style={{height: "5000px"}}>
      <OtherComponent locked={locked} />
    </div>
  );
}

export default PageComponent;

我不明白为什么在wheelListener中locked不是true/为什么状态没有更新。我可以使用引用而不是状态变量,但是 OtherComponent 将不会更新。我发现实现这项工作的唯一方法是使用状态和引用。相当丑陋。有什么建议吗?

javascript reactjs
1个回答
0
投票

由于闭包在 JS 中的工作方式,事件监听器的回调函数以及超时和间隔将保留状态的原始值,即使状态随后已更新。 Evenlistener/超时/间隔回调中的过时状态是 React 的常见问题。

对于事件侦听器,回调函数具有新状态值的唯一方法是在状态更新时删除然后重新添加事件侦听器。

这里的模式是我们如何确保事件侦听器始终具有状态的当前值。

const [myValue, setMyValue] = useState("hello")

useEffect(()=>{
  function handleEvent(){
  console.log(myValue)
  }
document.addEventListener("onScroll", handleEvent)
return()=>{
  document.revmoveEventListener("onScroll", handleEvent)
  }
},[myValue])

当组件安装时,事件监听器就会被添加。 然后每次

myValue
更新时,事件侦听器都会被删除,然后使用新状态重新添加。

因此,在您提供的代码中我们可以做这样的事情。

import { useState, useEffect, useRef } from 'react';

const OtherComponent = ({ locked }) => {
  return (
    <div style={{ position: 'fixed' }}>{locked ? 'Locked' : 'Unlocked'}</div>
  );
};

const PageComponent = () => {
  const [locked, setLocked] = useState(false);
  const mainRef = useRef(null);

  useEffect(() => {
    document.addEventListener('scroll', scrollListener, { passive: true });
  }, []);

  const scrollListener = () => {
    document.removeEventListener('scroll', scrollListener);
    setLocked(true);
  };

  useEffect(() => {
    if (locked) {
      mainRef.current.addEventListener('wheel', wheelEventListener, {
        passive: true,
      });
    }
    return () => {
      mainRef.current.removeEventListener('wheel', wheelEventListener);
    };
  }, [locked]);

  const wheelEventListener = () => {
    // locked always === false here, expecting true
    console.log('locked: ', locked);
  };

  return (
    <div ref={mainRef} style={{ height: '5000px' }}>
      <OtherComponent locked={locked} />
    </div>
  );
};

export default PageComponent;

在此实现中,当

locked
更改为 false 时,我们有一个
useEffect
将添加
wheel
事件侦听器。 然后,如果锁定更改回
true
,我们将删除事件侦听器。

在您的场景中,我们只监听布尔值,因此我们的添加/删除逻辑很简单。 但如果状态是另一种原语,或者数组/对象,那么何时添加事件侦听器的逻辑可能会更复杂一些。

© www.soinside.com 2019 - 2024. All rights reserved.