如何在异步函数内批量处理 React setState() 调用?
How to batch React setState() calls together inside an async function?
所以我 运行 遇到了一个问题,即 React 没有将异步函数内部的多个 setState() 调用批处理在一起(React 通常会在异步函数之外自动执行此操作)。这意味着当我的代码是 运行 时,多个连续的 setState() 调用相互冲突,第一个 setState() 导致组件更新,第二个 setState() 发生在组件有足够的时间重新安装和我收到错误 "Can't perform a React state update on an unmounted component"。我做了一些研究,发现这是预期的行为,但我没有找到任何关于如何解决该问题的信息。
这里有一些示例代码(不是 运行),只是为了形象化我想表达的意思。在这种情况下,行
设置数据(response.data);
和
设置加载(假);
冲突并导致错误。
我需要某种方法使这两个调用成为原子调用。
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
const Demo = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState({});
const fetchData = async () => {
setLoading(true);
const response = await callAPI();
if (response.status === 200) {
setData(response.data);
}
setLoading(false);
};
return (
<div>
<Button disabled={loading} onClick={() => fetchData()}>
Fetch Data
</Button>
<p>{JSON.stringify(data)}</p>
</div>
);
};
export default Demo;
我认为您可以避免将加载作为独立的布尔变量而不是组件状态的一部分。另一种方法是使用 useEffect
延迟 API 调用,直到 loading
变量发生变化。 已满足您的需求。最后,您可以使用 class 组件 - setState()
函数接受回调作为第二个参数 - 这是在挂钩到位之前进行原子状态更改的方式。
你可以将你的状态一起批处理成一个复合状态对象,然后像这样做...
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
const Demo = () => {
const [demoState, setDemoState] = useState({data: null, loading: false});
const fetchData = () => {
setDemoState({...demoState, loading: true});
callAPI()
.then( response => handleResponse(response)} );
}
const handleResponse = (response) => {
if (response.status === 200) {
setDemoState({data: response.data, loading: false});
} else {
setDemoState({...demoState, loading: false});
}
}
return (
<div>
<Button disabled={loading} onClick={() => fetchData()}>
Fetch Data
</Button>
<p>{JSON.stringify(demoState.data)}</p>
</div>
);
};
export default Demo;
您可以将加载和数据合并为一个状态。
并且也不是加载使用状态。
例如:
/** status possible values can be: "idle", "pending", "resolved", "rejected"*/
const [state, setState] = useState({status: "idle", data: null})
现在您可以轻松更改状态并根据该状态处理不同的事情
所以我 运行 遇到了一个问题,即 React 没有将异步函数内部的多个 setState() 调用批处理在一起(React 通常会在异步函数之外自动执行此操作)。这意味着当我的代码是 运行 时,多个连续的 setState() 调用相互冲突,第一个 setState() 导致组件更新,第二个 setState() 发生在组件有足够的时间重新安装和我收到错误 "Can't perform a React state update on an unmounted component"。我做了一些研究,发现这是预期的行为,但我没有找到任何关于如何解决该问题的信息。
这里有一些示例代码(不是 运行),只是为了形象化我想表达的意思。在这种情况下,行 设置数据(response.data); 和 设置加载(假); 冲突并导致错误。
我需要某种方法使这两个调用成为原子调用。
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
const Demo = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState({});
const fetchData = async () => {
setLoading(true);
const response = await callAPI();
if (response.status === 200) {
setData(response.data);
}
setLoading(false);
};
return (
<div>
<Button disabled={loading} onClick={() => fetchData()}>
Fetch Data
</Button>
<p>{JSON.stringify(data)}</p>
</div>
);
};
export default Demo;
我认为您可以避免将加载作为独立的布尔变量而不是组件状态的一部分。另一种方法是使用 useEffect
延迟 API 调用,直到 loading
变量发生变化。 setState()
函数接受回调作为第二个参数 - 这是在挂钩到位之前进行原子状态更改的方式。
你可以将你的状态一起批处理成一个复合状态对象,然后像这样做...
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
const Demo = () => {
const [demoState, setDemoState] = useState({data: null, loading: false});
const fetchData = () => {
setDemoState({...demoState, loading: true});
callAPI()
.then( response => handleResponse(response)} );
}
const handleResponse = (response) => {
if (response.status === 200) {
setDemoState({data: response.data, loading: false});
} else {
setDemoState({...demoState, loading: false});
}
}
return (
<div>
<Button disabled={loading} onClick={() => fetchData()}>
Fetch Data
</Button>
<p>{JSON.stringify(demoState.data)}</p>
</div>
);
};
export default Demo;
您可以将加载和数据合并为一个状态。 并且也不是加载使用状态。
例如:
/** status possible values can be: "idle", "pending", "resolved", "rejected"*/
const [state, setState] = useState({status: "idle", data: null})
现在您可以轻松更改状态并根据该状态处理不同的事情