在 promises 中正确地使用 setState 不更新状态
React not updating state with setState correctly inside promises
我正在尝试在单击按钮时从反应组件内的 api 加载更多数据。此数据应与已加载的数据合并。加载时我想显示一个微调器。我正在使用 axios 和 react hook 组件。
我的代码:
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
setData({...data, isLoadingMore: true})
axios
.get(uri)
.then(response => {
setData({
...data,
items: [...data.items, response.data.items]
})
})
.catch(error => {
setData({
...data,
error: 'An error occured'
})
})
.finally(() => {
setData({
...data,
isLoadingMore: false
})
})
}
我希望这首先显示微调器、加载新数据、将其与先前存在的数据合并并显示新的项目列表,但新数据没有合并。 ajax调用returns结果正确,所以没有问题。出乎我意料的是,如果我删除 .finally(..)
一切都会按预期进行,甚至微调器也会消失。
那么问题来了,setData是如何更新promises里面的state的呢?在我看来,遗漏 .finally(..)
一点都不清楚,因为 isLoadingMore
在代码中从未设置为 false 但它无论如何都会更新为 false
很难说,因为代码不完整,但我看到两个问题(假设缺失的 ]
只是问题中的错字):
- 您没有在
setData
通话中分散 response.data.items
。
- 您正在使用现有状态来设置新状态,而不是使用状态的回调版本来设置新状态 setter。文档对此不一致,但是你应该在基于现有状态设置状态时使用回调版本,除非你知道你只在某些情况下更新状态React 专门为其处理刷新更新的特定事件处理程序(如
click
)。
所以(见评论):
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
// *** Use callback
setData(current => ({...current, isLoadingMore: true}));
axios
.get(uri)
.then(response => {
// *** Use callback, spread response.data.items
setData(current => ({
...current,
items: [...current.items, ...response.data.items]
}));
})
.catch(error => {
// *** Use callback
setData(current => ({...current, error: 'An error occured'}));
})
.finally(() => {
// *** Use callback
setData(current => ({...current, isLoadingMore: false}));
});
}
}
但如果您使用这样的组合状态(更多内容见下文),我会将 isLoadingMore
的清除与其上方的内容结合起来。
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
setData(current => ({...current, isLoadingMore: true}));
axios
.get(uri)
.then(response => {
setData(current => ({
...current,
items: [...current.items, ...response.data.items],
isLoadingMore: false // ***
}));
})
.catch(error => {
setData(current => ({
...current,
error: 'An error occured',
isLoadingMore: false // ***
}));
});
}
}
但是:您应该对不同的状态项使用separate useState
calls。
const App = props => {
const [items, setItems] = useState([]);
const [loadingMore, setLoadingMore] = useState(false);
const [error, setError] = useState("");
// ...
function handleLoadMore(uri) {
setLoadingMore(true);
axios
.get(uri)
.then(response => {
setItems(currentItems => [...currentItems, ...response.data.items]);
})
.catch(error => {
setError('An error occured');
})
.finally(() => {
setLoadingMore(false);
});
}
// ...
}
注意这如何让更新更加离散,从而减少工作量(不会不断地重新传播所有状态项)。
我正在尝试在单击按钮时从反应组件内的 api 加载更多数据。此数据应与已加载的数据合并。加载时我想显示一个微调器。我正在使用 axios 和 react hook 组件。
我的代码:
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
setData({...data, isLoadingMore: true})
axios
.get(uri)
.then(response => {
setData({
...data,
items: [...data.items, response.data.items]
})
})
.catch(error => {
setData({
...data,
error: 'An error occured'
})
})
.finally(() => {
setData({
...data,
isLoadingMore: false
})
})
}
我希望这首先显示微调器、加载新数据、将其与先前存在的数据合并并显示新的项目列表,但新数据没有合并。 ajax调用returns结果正确,所以没有问题。出乎我意料的是,如果我删除 .finally(..)
一切都会按预期进行,甚至微调器也会消失。
那么问题来了,setData是如何更新promises里面的state的呢?在我看来,遗漏 .finally(..)
一点都不清楚,因为 isLoadingMore
在代码中从未设置为 false 但它无论如何都会更新为 false
很难说,因为代码不完整,但我看到两个问题(假设缺失的 ]
只是问题中的错字):
- 您没有在
setData
通话中分散response.data.items
。 - 您正在使用现有状态来设置新状态,而不是使用状态的回调版本来设置新状态 setter。文档对此不一致,但是你应该在基于现有状态设置状态时使用回调版本,除非你知道你只在某些情况下更新状态React 专门为其处理刷新更新的特定事件处理程序(如
click
)。
所以(见评论):
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
// *** Use callback
setData(current => ({...current, isLoadingMore: true}));
axios
.get(uri)
.then(response => {
// *** Use callback, spread response.data.items
setData(current => ({
...current,
items: [...current.items, ...response.data.items]
}));
})
.catch(error => {
// *** Use callback
setData(current => ({...current, error: 'An error occured'}));
})
.finally(() => {
// *** Use callback
setData(current => ({...current, isLoadingMore: false}));
});
}
}
但如果您使用这样的组合状态(更多内容见下文),我会将 isLoadingMore
的清除与其上方的内容结合起来。
const App = props => {
const [data, setData] = useState({ ... });
// ...
function handleLoadMore(uri) {
setData(current => ({...current, isLoadingMore: true}));
axios
.get(uri)
.then(response => {
setData(current => ({
...current,
items: [...current.items, ...response.data.items],
isLoadingMore: false // ***
}));
})
.catch(error => {
setData(current => ({
...current,
error: 'An error occured',
isLoadingMore: false // ***
}));
});
}
}
但是:您应该对不同的状态项使用separate useState
calls。
const App = props => {
const [items, setItems] = useState([]);
const [loadingMore, setLoadingMore] = useState(false);
const [error, setError] = useState("");
// ...
function handleLoadMore(uri) {
setLoadingMore(true);
axios
.get(uri)
.then(response => {
setItems(currentItems => [...currentItems, ...response.data.items]);
})
.catch(error => {
setError('An error occured');
})
.finally(() => {
setLoadingMore(false);
});
}
// ...
}
注意这如何让更新更加离散,从而减少工作量(不会不断地重新传播所有状态项)。