You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 10:37:17