[英]Setting state in React useEffect hook
在我的应用程序中,单击更改endpoint
状态的按钮后,我从我的 API 获取数据。 在获取数据之前,我想显示一个加载图标。 经过大量测试,我终于让它工作了。 这是它的样子:
const [endpoint, setEndpoint] = useState('products');
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
setLoading(true);
fetch(`/api/${endpoint}`, {
method: 'GET',
})
.then(res => res.json())
.then(data => {
setData(data);
})
.catch(e => {
setData([]);
})
.finally(() => setLoading(false));
}, [endpoint]);
const onSelect = (option) => {
setEndpoint(option);
}
return (
<>
<Sidebar onSelect={onSelect}/>
<div className="main">
<Banner />
{loading ? 'Loading...' : JSON.stringify(data)}
</div>
</>
);
尽管我得到了我想要的结果,但我对它们如何组合在一起有点困惑,因为我是 React 新手。 如果我错了,请纠正我,但这是我的理解:
setEndpoint
触发重新渲染,这会导致useEffect
执行,因为它依赖于endpoint
。 useEffect
中的每个设置状态调用也会导致重新渲染。 例如,当setLoading(true)
时,屏幕会重新渲染以显示“正在加载...”。 一旦loading
状态设置为true
,就会调用fetch
。 一旦 promise 被解决, setData(data)
就会导致另一个重新渲染。 但是,在调用setLoading(false)
之前,我的数据不会显示在屏幕上,再次重新渲染屏幕,显示我的数据。 所以总的来说,屏幕被重新渲染了 3 次(对吧?)。
我仍然感到困惑,因为我的印象是useEffect
之类的钩子是异步的,那么为什么它要等待状态设置后再继续执行下一行代码呢?
useEffect
挂钩回调是完全同步的。 你看到的是异步 Promise 链在工作。
useEffect(() => {
setLoading(true); // (3)
fetch(`/api/${endpoint}`, { // (4)
method: 'GET',
})
.then(res => res.json())
.then(data => {
setData(data); // (6)
})
.catch(e => {
setData([]); // (6)
})
.finally(() => setLoading(false)); // (7)
}, [endpoint]); // (2)
const onSelect = (option) => {
setEndpoint(option); // (1)
}
...
{loading ? 'Loading...' : JSON.stringify(data)} // (5), (8)
您解释为您的理解的内容在很大程度上是正确的。
endpoint
状态更新入队,回调函数完成并处理状态更新,触发重新渲染。useEffect
挂钩,检查其依赖关系。 由于endpoint
值更新, useEffect
回调被调用。loading
状态更新入队。 回调函数仍在运行。fetch
请求。 fetch
返回一个 Promise,代码启动一个 Promise 链。 这个链是异步的。 useEffect
挂钩回调现在完成,因为没有更多同步代码要运行。 处理loading
状态更新并触发重新渲染。data
状态更新入队并在链中返回 Promise。 处理data
状态更新并触发重新渲染。loading
状态更新入队。 处理loading
状态并触发重新渲染。所以总的来说,屏幕被重新渲染了 3 次(对吧?)。
根据我的统计,上面的逻辑可能触发了 4 次重新渲染,但 React 可能会因为任何其他原因重新渲染任意次数。 当 state 或 props 更新,或者父组件重新渲染时,React 组件会重新渲染。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.