javascript - 为什么 react 功能组件中的回调没有读取更新的状态值

标签 javascript reactjs react-hooks

我正在尝试使用交集观察器来实现无限滚动器,但我面临的问题是,在交集观察器的回调中,我无法读取当前“页面”和“列表”的最新值,因此我可以获取下一页的数据。

import ReactDOM from "react-dom";
import "./styles.css";
require("intersection-observer");

const pageSize = 30;
const threshold = 5;

const generateList = (page, size) => {
  let arr = [];
  for (let i = 1; i <= size; i++) {
    arr.push(`${(page - 1) * size + i}`);
  }

  return arr;
};

const fetchList = page => {
  return new Promise(resolve => {
    setTimeout(() => {
      return resolve(generateList(page, pageSize));
    }, 1000);
  });
};

let options = {
  root: null,
  threshold: 0
};

function App() {
  const [page, setPage] = useState(1);
  const [fetching, setFetching] = useState(false);
  const [list, setlist] = useState(generateList(page, pageSize));

  const callback = entries => {
    if (entries[0].isIntersecting) {
      observerRef.current.unobserve(
        document.getElementById(`item_${list.length - threshold}`)
      );
      setFetching(true);
/* at this point neither the 'page' is latest nor the 'list'
*they both have the initial states.
*/
      fetchList(page + 1).then(res => {
        setFetching(false);
        setPage(page + 1);
        setlist([...list, ...res]);
      });
    }
  };

  const observerRef = useRef(new IntersectionObserver(callback, options));

  useEffect(() => {
    if (observerRef.current) {
      observerRef.current.observe(
        document.getElementById(`item_${list.length - threshold}`)
      );
    }
  }, [list]);

  return (
    <div className="App">
      {list.map(l => (
        <p key={l} id={`item_${l}`}>
          {l}
        </p>
      ))}
      {fetching && <p>loading...</p>}
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

当前行为:“page”和“list”的值始终等于初始状态而不是最新值。第 2 页之后无限滚动不起作用

预期行为:在回调函数中,它应该读取状态“页面”和“列表”的更新值。

这是此演示的工作沙箱https://codesandbox.io/s/sweet-sun-rbcml?fontsize=14&hidenavigation=1&theme=dark

最佳答案

这里主要有两个问题,闭包和直接查询 DOM。

要解决闭包问题,请使用函数式 useState 和引用:

const listLengthRef = useRef(list.length);
const pageRef = useRef(page);

const callback = useCallback(entries => {
  if (entries[0].isIntersecting) {
    observerRef.current.unobserve(
      document.getElementById(`item_${listLengthRef.current - threshold}`)
    );
    setFetching(true);
    fetchList(pageRef.current + 1).then(res => {
      setFetching(false);
      setPage(page => page + 1);
      setlist(list => [...list, ...res]);
    });
  }
}, []);

const observerRef = useRef(new IntersectionObserver(callback, options));

useEffect(() => {
  listLengthRef.current = list.length;
}, [list]);

useEffect(() => {
  pageRef.current = page;
}, [page]);

虽然此代码有效,但您应该将 document.getElementById 替换为引用,在本例中,它将是对页面最后一个元素的引用。

Edit frosty-smoke-if4il

关于javascript - 为什么 react 功能组件中的回调没有读取更新的状态值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59589835/

相关文章:

javascript - 粘性导航栏-React

javascript - Angular 4 中的全局错误处理程序

javascript - 使用 ASP.NET MVC 5 应用程序设置 ReactJS

javascript - 如何更改对话框组件中的默认动画?

javascript - 当从父级过滤时,React 子组件会取代另一个子级的状态

javascript - JSON.stringify 在所有现代 JS 引擎上返回相同的字符串吗?

javascript - React 中类名的复杂条件

reactjs - react-admin 创建一个可以从菜单侧边栏访问的自定义页面

reactjs - 取消 Context.Consumer 创建的 useEffect 清理函数中的所有订阅

reactjs - 从 Ref 获取 React-Native 元素的高度