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
调用更新以前的状态。
我正在使用 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
调用更新以前的状态。