【问题标题】:React hooks - setState does not update state propertiesReact hooks - setState 不更新状态属性
【发布时间】:2020-01-16 13:54:17
【问题描述】:

我有一个来自滚动窗口对象的事件绑定。每次滚动时它都会被正确触发。直到现在一切正常。但是 setNavState 函数(这是我的 setState 函数)不会更新我的状态属性。

export default function TabBar() {
    const [navState, setNavState] = React.useState(
        {
            showNavLogo: true,
            lastScrollPos: 0
        });

    function handleScroll(e: any) {
        const currScrollPos = e.path[1].scrollY;
        const { lastScrollPos, showNavLogo } = navState;

        console.log('currScrollPos: ', currScrollPos); // updates accordingly to the scroll pos
        console.log('lastScrollPos: ', lastScrollPos); // last scroll keeps beeing 0
        if (currScrollPos > lastScrollPos) {
            setNavState({showNavLogo: false, lastScrollPos: currScrollPos});
        } else {
            setNavState({showNavLogo: true, lastScrollPos: currScrollPos});
        }
    }

    useEffect(() => {
        window.addEventListener('scroll', handleScroll.bind(this));
    }, []);

   ...
   }

所以我的问题是如何在此示例中相应地使用反应挂钩更新我的状态属性?

【问题讨论】:

  • 我不明白你为什么使用 setNavState 而不是 setState
  • @Skyentao,他使用的是功能组件,而不是类组件。他不能在功能组件中使用 setState

标签: javascript reactjs react-hooks


【解决方案1】:

这是因为闭包的工作原理。看,在初始渲染时,您声明 handleScroll 可以通过关闭访问初始 navState 和 setNavState。然后,您将使用 handleScroll 的 #1 版本订阅滚动。

接下来渲染您的代码会创建handleScroll 的#2 版本,它通过闭包指向最新的navState。但是你从不使用那个版本来处理滚动。

看,实际上不是你的处理程序“没有更新状态”,而是它用过时的值更新了它。

选项 1

在每次渲染时重新订阅

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
});

选项 2

仅在数据更改时使用useCallback 重新创建处理程序,并且仅在重新创建回调时重新订阅

const handleScroll = useCallback(() => { ... }, [navState]);
useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, [handleScroll]);

看起来效率更高,但更混乱/可读性更差。所以我更喜欢第一个选项。

您可能想知道为什么我将navState 包含在依赖项中,而不包含setNavState。原因是 - setter(从 useState 返回的回调) is guaranteed 在每个渲染上都引用相同。

[UPD] 忘记了 setter 的功能版本。当我们不想引用另一个 useState 的数据时,它肯定会正常工作。所以不要错过投票answer by giorgim

【讨论】:

    【解决方案2】:

    只需为 useEffect 添加依赖和清理

    function TabBar() {
        const [navState, setNavState] = React.useState(
            {
                showNavLogo: true,
                lastScrollPos: 0
            });
    
        function handleScroll(e) {
            const currScrollPos = e.path[1].scrollY;
            const { lastScrollPos, showNavLogo } = navState;
    
            console.log('showNavLogo: ', showNavLogo);
            console.log('lastScrollPos: ', lastScrollPos);
            if (currScrollPos > lastScrollPos) {
                setNavState({showNavLogo: false, lastScrollPos: currScrollPos});
            } else {
                setNavState({showNavLogo: true, lastScrollPos: currScrollPos});
            }
        }
    
        React.useEffect(() => {
            window.addEventListener('scroll', handleScroll.bind(this));
            
            return () => {
              window.removeEventListener('scroll', handleScroll.bind(this));
            }
        }, [navState]);
    
         return (<h1>scroll example</h1>)
       }
       
       ReactDOM.render(<TabBar />, document.body)
    h1 {
      height: 1000px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

    【讨论】:

    • @giorgim navState 仅在初始渲染时由文字对象构造。那么只有在setNavState被调用后才会改变
    【解决方案3】:

    我认为您的问题可能是您注册了一次该侦听器(例如componendDidMount),现在每次由于滚动而调用该侦听器函数时,由于关闭,您指的是navState 的相同值.

    把它放在你的监听器函数中,应该可以让你访问当前状态:

    setNavState(ps => {
          if (currScrollPos > ps.lastScrollPos) {
            return { showNavLogo: false, lastScrollPos: currScrollPos };
          } else {
            return { showNavLogo: true, lastScrollPos: currScrollPos };
          }
        });
    

    【讨论】:

      猜你喜欢
      • 2021-07-20
      • 2021-11-13
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 2019-08-08
      • 1970-01-01
      相关资源
      最近更新 更多