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

时间:2020-07-01 09:11:38

标签: javascript reactjs state use-state

我正在使用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;

1 个答案:

答案 0 :(得分:2)

您的问题在于您的状态更新:

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

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

要对先前状态进行更新,最好的方法是使用函数作为参数调用setResults函数,该函数将先前状态作为自己的参数。这样可以确保状态更新是连续的(每次更新在上一个更新完成之后进行)。

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

在这里,我还使用了concat函数,该函数将两个数组合并为一个数组并返回结果。这样,您就不会使用push调用来更新以前的状态。