首页 > 解决方案 > React 钩子值正确呈现但未在 addEventListener 的回调函数中更新

问题描述

我正在功能组件中使用钩子实现无限滚动,我遇到了一个问题,我无法访问滚动事件回调函数中的更新值,这是我的代码:

// Get a hook function
const {useState, useEffect} = React;

const initItems = ['First', 'Second','Third','Fourth','Fifth']

const Example = ({title}) => {
const [posts, setPosts] = useState({
        data: [],
        page: 1
    });

    console.log('FN RENDER')

    const handleScroll = (e) => {

        const { scrollHeight, scrollTop, clientHeight } = e.target

      
        const bottom = scrollHeight - scrollTop === clientHeight;

        if (bottom) {
        
            console.log('page', posts.page) // page stays 1


            const nextPage = posts.page + 1

             setPosts(p => ({
                  data: [...p.data, 'Append'],
                  page: p.page + 1
              }))
        }
    }

    useEffect(() => {

        console.log('USEEFFECT RENDER')

        setPosts(p => ({
            data: initItems,
            page: 1
        }))
        
        
//using ScrollBox for demo, originally I'm handling the scroll event on the document: document.addEventListener            
document.getElementById("ScrollBox").addEventListener('scroll',handleScroll);

        return () => document.getElementById("ScrollBox").removeEventListener('scroll', handleScroll);
    }, []);


  return (
    <div> 
    <p>Page:{posts.page}</p>
    
    <div id="ScrollBox" class="scroll-box">
      {
         posts.data.map(k=> <p>{k} </p>)
      }
    </div>
    
    </div>
    
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
#ScrollBox{
    border:1px solid red;
    height:100px;
    overflow:auto;
    width:400px;

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="react"></div>

如果您查看控制台,则在正确呈现时,值会page保持不变。1我曾经useEffect附上事件。我怎样才能在里面获得正确的更新值handleScroll

- 编辑 -

如何在 的回调中获取更新的值addEventListener#ScrollBox对于问题演示,我需要检测 scroll on document,这就是我使用事件侦听器的原因。

标签: javascriptreactjsreact-hooks

解决方案


useEffect函数仅在第二个参数发生更改时触发。由于您有一个空的第二个参数,因此它永远不会更新,并且由于它永远不会更新,因此无论您更新多少次状态,您的事件处理程序都会从​​第一次渲染中保持陈旧的闭包。

您可以通过直接在渲染函数内部而不是在useEffect. 无论如何,您都不应该在 react 中进行直接的 DOM 操作。

// Get a hook function
const {useState, useEffect} = React;

const initItems = ['First', 'Second','Third','Fourth','Fifth']

const Example = ({title}) => {
const [posts, setPosts] = useState({
        data: [],
        page: 1
    });

    console.log('FN RENDER')

    const handleScroll = (e) => {

        const { scrollHeight, scrollTop, clientHeight } = e.target

      
        const bottom = scrollHeight - scrollTop === clientHeight;

        if (bottom) {
        
            console.log('page', posts.page) // page stays 1


            const nextPage = posts.page + 1

             setPosts(p => ({
                  data: [...p.data, 'Append'],
                  page: p.page + 1
              }))
        }
    }

    useEffect(() => {

        console.log('USEEFFECT RENDER')
        // <!-- No event handler here -->
        setPosts(p => ({
            data: initItems,
            page: 1
        }))
        
        
    }, []);


  return (
    <div> 
    <p>Page:{posts.page}</p>
    
    <!-- NOTE THE EVENT HANDLER HERE -->
    <div id="ScrollBox" class="scroll-box"
         onScroll={handleScroll}
>
      {
         posts.data.map(k=> <p>{k} </p>)
      }
    </div>
    
    </div>
    
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
#ScrollBox{
    border:1px solid red;
    height:100px;
    overflow:auto;
    width:400px;

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="react"></div>


推荐阅读