React技术问题:如何等待状态更新后再执行useEffect
问题:等待自定义Hook加载完成后执行滚动逻辑
我有一个名为ItemsIndex的组件,它通过自定义Hook useItems从API获取数据。但组件里的useEffect会先执行,此时DOM还没渲染出可以滚动到视图中的元素,导致没法通过ID找到目标元素完成滚动。想请教怎么等待useItems加载完成,让DOM渲染出带itemTree元素和对应ID的内容,这样应该就能解决滚动问题了。
ItemsIndex组件代码
const ItemsIndex = () => { const itemsContext = useContext(ItemsContext); const [uploaditemModalOpen, setUploaditemModalOpen] = useState(false); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [deleteitem, setDeleteitem] = useState(""); const { error, loading, items, itemId, setItems } = useItems(); if (error) { console.error(error); } const resolvedItems = loading ? MOCK_ITEMS : items; useEffect(() => { // 解码URL中的itemDownload查询参数 const params = new URLSearchParams(window.location.search); const itemDownload = params.get("itemDownload"); if (!itemDownload) return; const decodedItemPath = decodeURIComponent(itemDownload); try { scrollToItem(decodedItemPath); // 找不到对应ID的元素,因为还没渲染 } catch (error) { console.error(error); } }, []);
useItems Hook代码
export const useItems = () => { const [items, setItems] = useState<ItemTree>({ content: {} }); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const { scope, groupData } = useSelector(state); const itemId = scope.ItemId; const groupHierarchy = groupData?.path || undefined; useEffect(() => { const cachedItems = StorageUtils.get(Items_KEY); if (cachedItems) { setItems(JSON.parse(cachedItems)); return; } const retrieveVisibleItems = async () => { try { setLoading(true); const { authItems } = await fetchItems(itemId, groupHierarchy); setItems(authItems); StorageUtils.set(Items_KEY, JSON.stringify(authItems)); } catch (err) { setError(err); } finally { setLoading(false); } }; retrieveVisibleItems(); }, [itemId, groupHierarchy]); return { error, loading, items, itemId, setItems };
解决方案
1. 监听loading状态和items数据变化
把原useEffect的依赖数组加上loading和items,确保只有当数据加载完成(loading为false)且items已就绪时,才执行滚动逻辑。这样能覆盖API请求和缓存加载两种场景。
修改后的useEffect代码:
useEffect(() => { const params = new URLSearchParams(window.location.search); const itemDownload = params.get("itemDownload"); // 没有参数或仍在加载时直接返回 if (!itemDownload || loading) return; const decodedItemPath = decodeURIComponent(itemDownload); try { scrollToItem(decodedItemPath); } catch (error) { console.error(error); } }, [loading, items]); // 依赖loading和items,数据就绪后触发
2. 可选:用useLayoutEffect确保DOM已更新
如果滚动逻辑还是存在元素未渲染的问题,可以把useEffect换成useLayoutEffect。它会在DOM更新完成后同步执行,比useEffect更早触发,能保证目标元素已经渲染到DOM中。
修改后的代码:
useLayoutEffect(() => { const params = new URLSearchParams(window.location.search); const itemDownload = params.get("itemDownload"); if (!itemDownload || loading) return; const decodedItemPath = decodeURIComponent(itemDownload); try { scrollToItem(decodedItemPath); } catch (error) { console.error(error); } }, [loading, items]);
内容的提问来源于stack exchange,提问作者Dzsonah
相关产品推荐
相关产品推荐

