首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么react函数组件中的回调没有读取更新的状态值

为什么react函数组件中的回调没有读取更新的状态值
EN

Stack Overflow用户
提问于 2020-01-04 10:27:17
回答 3查看 1.4K关注 0票数 1

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

代码语言:javascript
复制
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);

当前行为:‘页面’和'list‘的值总是等于初始状态,而不是最新值。无限卷轴在第2页后不起作用。

预期行为:回调函数中的应该读取状态'page‘和'list’的更新值。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-01-04 16:23:44

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

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

代码语言:javascript
复制
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]);

尽管这段代码可以工作,但是您应该用reference替换document.getElementById,在本例中,它将是对页面最后一个元素的引用。

票数 2
EN

Stack Overflow用户

发布于 2020-01-04 16:11:59

您可以使用React setState 回调法来保证您将收到前面的值。

按以下方式更新您的callback函数,它应该可以工作。

代码语言:javascript
复制
const callback = entries => {
  if (entries[0].isIntersecting) {
    setFetching(true);
    setPage(prevPage => {
      fetchList(prevPage + 1).then(res => {
        setFetching(false);
        setlist(prevList => {
          observerRef.current.unobserve(document.getElementById(`item_${prevList.length - threshold}`));
          return ([...prevList, ...res]);
        });
      })
      return prevPage + 1;
    })
  }
};
票数 1
EN

Stack Overflow用户

发布于 2020-01-04 16:21:57

我认为问题是由于裁判一直引用老观察者。每次更新依赖项时,都需要刷新观察者。它与js中的闭包有关。我会更新你的应用程序,将回调移动到useEffect中

代码语言:javascript
复制
function App() {
  const [page, setPage] = useState(1);
  const [fetching, setFetching] = useState(false);
  const [list, setlist] = useState(generateList(page, pageSize));


  const observerRef = useRef(null);

  useEffect(() => {
    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.
     */
        console.log(page, list);
        fetchList(page + 1).then(res => {
          setFetching(false);
          setPage(page + 1);
          setlist([...list, ...res]);
        });
      }
    };
    observerRef.current = new IntersectionObserver(callback, options);

    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>
  );
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59589835

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档