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

React严格模式下Zustand加载状态异常问题及最优方案问询

针对Zustand + React严格模式的加载状态优化方案

1. 用请求标识跟踪有效请求

给每个请求生成唯一ID,仅当当前请求是最新的那个时,才允许更新loading状态,旧请求的状态变更会被直接忽略,避免干扰后续请求的状态展示。

示例代码:

catsStore.ts

import { create } from 'zustand';

type Cat = { id: string; name: string };
type CatsState = {
  cats: Cat[];
  loading: boolean;
  latestRequestId: string | null;
};

export const useCatsStore = create<CatsState>((set) => ({
  cats: [],
  loading: false,
  latestRequestId: null,
  getCats: async () => {
    const requestId = crypto.randomUUID();
    set({ loading: true, latestRequestId: requestId });
    
    try {
      const controller = new AbortController();
      const res = await fetch('/api/cats', { signal: controller.signal });
      const cats = await res.json();
      
      // 仅最新请求能更新状态
      set((state) => {
        if (state.latestRequestId === requestId) {
          return { cats, loading: false };
        }
        return {};
      });
    } catch (err) {
      // 忽略AbortError,且仅最新请求能修改loading
      if (!(err instanceof DOMException && err.name === 'AbortError')) {
        set((state) => {
          if (state.latestRequestId === requestId) {
            return { loading: false };
          }
          return {};
        });
      }
    }
  },
}));

2. 组件层结合useEffect清理旧请求

利用React useEffect的清理函数,在组件重新渲染时主动取消前一次请求,同时让Zustand只响应未被取消的请求状态更新。

Cats.tsx

import { useCatsStore } from './catsStore';
import { useEffect } from 'react';

export const Cats = () => {
  const { cats, loading, getCats } = useCatsStore();

  useEffect(() => {
    const controller = new AbortController();
    const fetchCats = async () => {
      try {
        await getCats(controller.signal);
      } catch (err) {
        if (!(err instanceof DOMException && err.name === 'AbortError')) {
          console.error('Failed to fetch cats:', err);
        }
      }
    };

    fetchCats();

    return () => controller.abort();
  }, [getCats]);

  return (
    <div>
      {loading ? <p>Loading...</p> : (
        <ul>
          {cats.map(cat => <li key={cat.id}>{cat.name}</li>)}
        </ul>
      )}
    </div>
  );
};

对应的catsStore中更新getCats方法:

// catsStore.ts中修改getCats
getCats: async (signal: AbortSignal) => {
  const requestId = crypto.randomUUID();
  set({ loading: true, latestRequestId: requestId });
  
  try {
    const res = await fetch('/api/cats', { signal });
    const cats = await res.json();
    
    set((state) => state.latestRequestId === requestId ? { cats, loading: false } : {});
  } catch (err) {
    if (!(err instanceof DOMException && err.name === 'AbortError')) {
      set((state) => state.latestRequestId === requestId ? { loading: false } : {});
    }
  }
},

3. 封装通用加载状态管理Hook

把请求和状态跟踪逻辑抽成通用Hook,统一处理严格模式下的重复渲染问题,避免重复代码。

示例通用Hook:

import { useState, useCallback } from 'react';

export const useFetchWithLoading = <T>(fetchFn: (signal: AbortSignal) => Promise<T>) => {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [latestRequestId, setLatestRequestId] = useState<string | null>(null);

  const execute = useCallback(async () => {
    const requestId = crypto.randomUUID();
    setLatestRequestId(requestId);
    setLoading(true);
    setError(null);
    
    const controller = new AbortController();
    
    try {
      const result = await fetchFn(controller.signal);
      if (requestId === latestRequestId) {
        setData(result);
        setLoading(false);
      }
    } catch (err) {
      if (!(err instanceof DOMException && err.name === 'AbortError')) {
        if (requestId === latestRequestId) {
          setError(err as Error);
          setLoading(false);
        }
      }
    }
    
    return () => controller.abort();
  }, [fetchFn, latestRequestId]);

  return { loading, data, error, execute };
};

在组件中直接调用该Hook,配合Zustand使用即可简化状态管理。

4. 加载状态前置判断避免重复请求

在Zustand的请求方法中,先检查当前是否处于加载状态,如果已经在加载则直接返回,避免重复发起请求。

// catsStore.ts中修改getCats
getCats: async () => {
  // 已在加载则跳过
  const currentState = useCatsStore.getState();
  if (currentState.loading) return;
  
  set({ loading: true });
  
  try {
    const res = await fetch('/api/cats');
    const cats = await res.json();
    set({ cats, loading: false });
  } catch (err) {
    if (!(err instanceof DOMException && err.name === 'AbortError')) {
      set({ loading: false });
    }
  }
},

这种方式简单直接,适合单一请求的场景,无需额外生成请求ID。


内容的提问来源于stack exchange,提问作者Anton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 15:21:12