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]);
问题根源
access_token引用不稳定:每次组件渲染都会重新执行JSON.parse(localStorage.getItem("xibo")),返回新对象实例,导致access_token的引用每次都不相同,触发useEffect重复执行。- 组件反复卸载/挂载:Dashboard组件通过
isLoading状态控制是否渲染DisplayList。getDisplays执行时isLoading变为true,DisplayList被卸载;请求完成后isLoading变为false,DisplayList重新挂载,再次触发useEffect,形成循环。 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
相关产品推荐
相关产品推荐

