【问题标题】:React state change does not result in a rerenderReact 状态更改不会导致重新渲染
【发布时间】:2020-07-01 09:11:38
【问题描述】:

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

【问题讨论】:

  • 您没有正确更新状态:与其“复制”results 状态(实际上不会像那样复制),您应该调用 setResults结果作为参数:setResults(prevResults =&gt; prevResults.concat(data.results)); 你能试试吗?

标签: javascript reactjs state use-state


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 你应该添加const resultsCopy = [...results]选项,因为它是在js中浅拷贝的方式,无论如何很好的答案
  • @HagaiHarari 我打算去,但决定不去,因为这仍然会导致不一致:正如你所说,它只是一个浅拷贝(这里不是问题,但是改变数组的对象会是),并且在逐页加载数据页面时确保状态更新的顺序性很重要。
猜你喜欢
  • 2022-12-21
  • 2015-11-03
  • 2018-11-29
  • 2020-10-03
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 2021-11-24
  • 2019-02-27
相关资源
最近更新 更多