React组件出现Too many re-renders错误:点击通知展示详情功能的问题排查与解决
解决Too many re-renders错误与无限渲染问题
我来帮你排查并解决这个问题,咱们一步步拆解核心原因和修复方案:
1. 修复Notifications组件的useEffect异步写法错误
React的useEffect回调函数不能直接是async函数——因为async函数会返回Promise,而useEffect期望返回的是清理函数(function)或者undefined。这种写法会干扰React的状态更新逻辑,间接引发重渲染异常。
修复代码:
const Notification = (hospital) => { const [users, setUsers] = useState([]); const [search, setSearch] = useState(null); const [status, setStatus] = useState(null); const [notifDetails, setNotification] = useState(null); // 给初始值,避免undefined useEffect(() => { // 把异步逻辑包裹在内部函数中 const fetchNotifications = async () => { try { const res = await axios.get("/notifications"); const result = res.data; setUsers(result); // 加个判断,避免result为空时取result[0]报错 if (result.length > 0) { setNotification(result[0]); } } catch (err) { console.error(err); } }; fetchNotifications(); }, []); return( {/* 用Fragment包裹多个顶级元素,React要求只能返回一个根元素 */} <> <div className="hospital-notif-container"> {filteredList(users, status, search).map((details, index) => { for (var i = 0; i < details.receiver.length; i++) { if ( (details.receiver[i].id === hospital.PK || details.receiver[i].id === "others") && details.sender.id !== hospital.PK ) { return ( <div className="hospital-notif-row" key={index} onClick={() => setNotification(details)} > <div className="hospital-notif-row"> {details.name} </div> </div> ); } } return null; })} </div> <NotificationDetails details={notifDetails} /> </> ); }
2. 修复NotificationDetails组件的空值处理逻辑
你的notifDetails初始值是undefined,导致NotificationDetails组件中执行Object.keys(details)时直接抛出Cannot convert undefined or null to object错误。这个错误会触发组件重新渲染,进而再次执行useEffect,形成无限循环。
修复代码:
const NotificationDetails = ({ details }) => { const [loading, setLoading] = useState(true); useEffect(() => { // 先判断details是否存在,再检查属性数量 if (details && Object.keys(details).length !== 0) { setLoading(false); } else { // 当details为空时,重置loading状态 setLoading(true); } }, [details]); // 额外加一层判断,避免details为undefined时渲染报错 if (!loading && details) { return ( <> <div className="hospital-details-container"> <h2>{details.sender.name}</h2> </div> </> ); } else { return (<div>Loading</div>); } };
3. 优化:避免不必要的重渲染(可选)
如果filteredList是纯函数(输入相同则输出相同),可以用useMemo缓存其结果,避免每次组件渲染都重新生成数组,减少不必要的重渲染:
import { useMemo } from 'react'; // 在Notification组件内部添加 const filteredNotifications = useMemo(() => { return filteredList(users, status, search); }, [users, status, search]); // 只有依赖项变化时才重新计算 // 替换原map中的filteredList调用 {filteredNotifications.map((details, index) => { // 原循环逻辑不变 })}
关于“一直使用result[0]无限渲染”的解释
当你在Notifications组件的useEffect中设置setNotification(result[0])后,NotificationDetails的details属性更新,触发其useEffect执行setLoading(false)。但如果之前的空值处理错误没有修复,每次渲染都会抛出异常,React会尝试重新渲染组件,从而形成“无限渲染”的循环。修复上述问题后,这个循环就会被打破。
内容的提问来源于stack exchange,提问作者Also James
相关产品推荐
相关产品推荐

