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

React中Firestore数据渲染异常:首页数据需跳转后才加载

React应用Firestore数据加载异常问题排查与解决

问题现象

首页的Firestore数据仅在跳转到其他页面后返回首页时才会加载,初始进入首页时拿到的是空数组。

function Menu() {
  const [data, setData] = useState([]);
  const { user } = UserAuth();
  const fetchProduct = async () => {
      const querySnapshot = await getDocs(collection(db, "langcards-db"));
      const arr = [];
      querySnapshot.forEach((doc) => {
        arr.push({
          ...doc.data(),
          id: doc.id,
        });
      });
      setData(arr);
  }
  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([])
    }
  }, []);
  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([])
    }  
  }, [user]);

 
  return (
    <div className="content">
      <Routes>
        <Route
          path="/"
          element={<Layout onSearch={() => onSearchF()} data={data} />}
        >
          <Route index element={<Home  data={data} num={data.length} />}/> 
          <Route
            path="saved"
            element={<Saved data={data} setData={setData} />}
          />            
        </Route>
      </Routes>
    </div>
  );
}
export default Menu;

Home组件代码

const shuffle = (arr) => arr.sort(() => Math.random() - 0.5);
function Home({ data, num }) {
  const [newArr, setNewArr] = useState(shuffle([...data]));
  const [i, setI] = useState(0);

  const randomCard = useMemo(() => {
    if (!newArr || i < 0 || i >= newArr.length) return undefined;
    return newArr[i];
  }, [newArr, i]);

  const randomizeCard = () => {
    if (i < newArr.length - 1) {
      setI(i + 1);
    } else {
      setNewArr(shuffle([...data]));
      setI(0);
    }
  };

  return (
    <div>
      <div className="home-inner">
        {num >= 1 && randomCard ? (
          <div className="home-stat">
            <p className='saved-stat'>
              Saved words: <span>{num}</span>
            </p>
            <div onClick={randomizeCard} className="home-random">
              <div className="hr-card">
                <p className="randomword">{randomCard.word}</p>
                <p className="randomtranslate">{randomCard.translate}</p>
              </div>
            </div>
            <Link className="home-btn main-btn" to="/saved">
              See all
            </Link>
          </div>
        ) : (
          <div>
            {" "}
            <div className="oops-section">
            <Link className="main-btn" to="/addcard">
              Add first word
            </Link>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

代码错误排查

  1. Menu组件存在冗余useEffect
    两个逻辑完全一致的useEffect,一个依赖空数组,一个依赖user,会导致初始渲染时重复发起数据请求,属于冗余代码。

  2. Home组件newArr未响应data更新
    newArr的初始值仅在组件第一次挂载时计算,当Menu组件中的data从空数组加载完成后,Home组件的newArr不会自动更新。只有跳转回来重新挂载Home时,才会用新的data初始化newArr,这就是初始首页不显示数据的核心原因。

解决方案

1. 合并Menu组件的useEffect

保留一个依赖user的useEffect即可,初始渲染时也会执行:

useEffect(() => {
  if(user){
    fetchProduct();
  }else{
    setData([])
  }
}, [user]);

2. 让Home组件newArr响应data变化

添加useEffect监听data变化,自动更新洗牌后的数组:

function Home({ data, num }) {
  const [newArr, setNewArr] = useState(shuffle([...data]));
  const [i, setI] = useState(0);

  // 监听data变化,重新洗牌并重置索引
  useEffect(() => {
    setNewArr(shuffle([...data]));
    setI(0);
  }, [data]);

  const randomCard = useMemo(() => {
    if (!newArr || i < 0 || i >= newArr.length) return undefined;
    return newArr[i];
  }, [newArr, i]);

  // 其余代码不变
}

3. 可选:添加加载状态优化体验

在Menu组件中加入加载状态,避免初始显示错误提示:

function Menu() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const { user } = UserAuth();
  
  const fetchProduct = async () => {
    setIsLoading(true);
    try {
      const querySnapshot = await getDocs(collection(db, "langcards-db"));
      const arr = [];
      querySnapshot.forEach((doc) => {
        arr.push({
          ...doc.data(),
          id: doc.id,
        });
      });
      setData(arr);
    } catch (err) {
      console.error("加载数据失败:", err);
    } finally {
      setIsLoading(false);
    }
  }

  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([]);
      setIsLoading(false);
    }
  }, [user]);

  return (
    <div className="content">
      <Routes>
        <Route
          path="/"
          element={<Layout onSearch={() => onSearchF()} data={data} />}
        >
          <Route index element={<Home data={data} num={data.length} isLoading={isLoading} />}/> 
          <Route
            path="saved"
            element={<Saved data={data} setData={setData} />}
          />            
        </Route>
      </Routes>
    </div>
  );
}

在Home组件中处理加载状态:

function Home({ data, num, isLoading }) {
  if (isLoading) {
    return <div>加载中...</div>;
  }

  // 原渲染内容
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 15:55:18