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

React中useEffect空依赖数组仍无限循环问题求助

React useEffect 无限循环问题解决办法

问题概述

页面挂载时使用useEffect派发Redux动作填充状态,无论设置空依赖数组,还是添加access_token和dispatch作为依赖,均陷入无限循环。

用户原始代码片段:

const token = JSON.parse(localStorage.getItem("xibo"));
const access_token = token.access_token;

useEffect(() => {
    dispatch(getDisplays(access_token));
}, []);

添加依赖后仍未解决:

const token = JSON.parse(localStorage.getItem("xibo"));
const access_token = token.access_token;

useEffect(() => {
    dispatch(getDisplays(access_token));
}, [access_token, dispatch]);

问题根源

  1. access_token引用不稳定:每次组件渲染都会重新执行JSON.parse(localStorage.getItem("xibo")),返回新对象实例,导致access_token的引用每次都不相同,触发useEffect重复执行。
  2. 组件反复卸载/挂载:Dashboard组件通过isLoading状态控制是否渲染DisplayList。getDisplays执行时isLoading变为true,DisplayList被卸载;请求完成后isLoading变为false,DisplayList重新挂载,再次触发useEffect,形成循环。
  3. getToken重复触发:Dashboard中useEffect在user存在时就会派发getToken,该动作会更新localStorage和Redux的xibo状态,导致DisplayList重新渲染,进而触发useEffect。

解决办法

1. 稳定access_token的引用

直接从Redux状态获取token,避免每次渲染重新解析localStorage:

const { xibo } = useSelector((store) => store.xibo);
const access_token = xibo?.access_token;

或者用useMemo缓存解析结果:

const token = useMemo(() => JSON.parse(localStorage.getItem("xibo")), []);
const access_token = token?.access_token;

2. 调整组件渲染逻辑

修改Dashboard的渲染逻辑,避免DisplayList反复被卸载和挂载:

// Dashboard组件return部分
return (
  <div className="col py-3">
    <div className="d-flex dash_dsp_list">
      {isLoading ? <WaveLoading /> : <DisplayList />}
    </div>
  </div>
);

或者让DisplayList自身管理加载状态,Dashboard不再控制其渲染。

3. 优化getToken的触发时机

仅在user存在且xibo为空时触发getToken,避免重复调用:

// Dashboard组件中的useEffect
useEffect(() => {
  if (isError) {
    toast.error(message);
  }
  if (user && !xibo) {
    dispatch(getToken());
  }
}, [user, isError, message, dispatch, xibo]);

4. 使用useCallback稳定动作引用(可选)

用useCallback包裹派发动作,确保引用稳定:

const fetchDisplays = useCallback(() => {
  if (access_token) {
    dispatch(getDisplays(access_token));
  }
}, [access_token, dispatch]);

useEffect(() => {
  fetchDisplays();
}, [fetchDisplays]);

修改后的DisplayList组件示例

import React, { useEffect } from "react";
import { WaveLoading } from "react-loadingg";
import { useDispatch, useSelector } from "react-redux";
import { getDisplays } from "../features/xiboAuth/xiboSlice";

const DisplayList = () => {
  const dispatch = useDispatch();
  const { isLoading, xibo, displays } = useSelector((store) => store.xibo);
  
  const access_token = xibo?.access_token;

  useEffect(() => {
    if (access_token) {
      dispatch(getDisplays(access_token));
    }
  }, [access_token, dispatch]);

  return (
    <>
      {isLoading ? (
        <WaveLoading />
      ) : (
        <div className="container-fluid">
          <div className="title_table">
            <h3>List of displays</h3>
          </div>
          <table className="table table-striped">
            <thead>
              <tr>
                <th className="col">#</th>
                <th className="col">name</th>
                <th className="col">type</th>
                <th className="col">ip</th>
              </tr>
            </thead>
            <tbody>
              {displays?.map((display, index) => (
                <tr key={display.id}>
                  <td>{index + 1}</td>
                  <td>{display.name}</td>
                  <td>{display.type}</td>
                  <td>{display.ip}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </>
  );
};

export default DisplayList;

内容的提问来源于stack exchange,提问作者Alvaro Augusto Fernandes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:57:50