React 状态更改不会导致重新渲染

React state change does not result in a rerender

我正在使用 React Infinite Scroller 呈现帖子列表。

我的加载更多结果函数被调用了 3 次,因此结果更新了 3 次(可以看到 console.log)

我用以下方式呈现帖子:

{results.map((result) => (
    <Widget data={result} key={result.id} />
))}

这会更新前两次结果,但是当状态第三次更改时,它不会再呈现任何结果。

我的完整代码:

import React, { useState } from "react";
import InfiniteScroll from "react-infinite-scroller";
import LoadingIndicator from "./loading";
import { request } from "../communication";

const InfiniteResults = ({ endpoint, widget }) => {
  let [results, setResults] = useState([]);
  let [hasMoreResults, setHasMoreResults] = useState(true);

  const Widget = widget;

  function loadMoreResults(page) {
    // prevent from making more requests while waiting for the other one to complete
    setHasMoreResults(false);

    request
      .get(endpoint, {
        offset: page * 10,
      })
      .then(function gotResults(response) {
        const data = response.data;

        const resultsCopy = results;
        data.results.forEach(function saveResultToState(result) {
          resultsCopy.push(result);
        });
        setResults(resultsCopy);
        console.log(resultsCopy);
        console.log(results);

        if (!data.next) {
          setHasMoreResults(false);
        } else {
          setHasMoreResults(true);
        }
      });
  }

  return (
    <InfiniteScroll
      pageStart={-1}
      loadMore={loadMoreResults}
      hasMore={hasMoreResults}
      loader={<p key={0}>Loading...</p>}
    >
      {results.map((result) => {
        console.log("rerender");
        return <Widget data={result} key={result.id} />;
      })}
    </InfiniteScroll>
  );
};

export default InfiniteResults;

您的问题出在您的状态更新上:

const resultsCopy = results;
data.results.forEach(function saveResultToState(result) {
  resultsCopy.push(result);
);
setResults(resultsCopy);

当您执行 const resultsCopy = results; 时,您实际上并没有复制数组;您只是用另一个名称引用同一个数组。这意味着当您开始向其中添加元素时,您是将它们直接添加到由 React 的 useState 钩子控制的元素中。所有状态更新都应通过 setState(在您的情况下为 setResults)函数完成 useState returns.

要对之前的状态执行更新,最好的方法是调用 setResults 函数,将函数作为参数,将之前的状态作为自己的参数。这确保了状态更新是连续的(每次更新都发生在前一次更新完成之后)。

setResults(prevState => prevState.concat(data.results));
// or
setResults(function (prevState) {
  return prevState.concat(data.results);
});

这里我还利用了concat函数,将两个数组合二为一,returns结果。这样你就不会用 push 调用更新以前的状态。