React实现Read More功能报错:过多重渲染,React限制渲染次数避免无限循环
问题修复方案
你代码的核心问题出在异步状态更新的时序错误、不符合React规范的状态使用方式,以及边界判断遗漏,具体修改如下:
首先修复App.js的冗余请求问题
你给Lists函数的useCallback依赖填了notifs,会导致每次通知列表更新后都重复请求接口,直接把依赖改为空数组即可,修改后代码:
import React, { useEffect, useState, useCallback } from 'react'; import Box from '@material-ui/core/Box'; import { notificationsApi } from './fakeAPI'; import ReadMore from './ReadMore'; const Notifications = () => { const [notifs, setNotifs] = useState(null); const Lists = useCallback(async () => { try { const notif = await notificationsApi.getNotifications(); setNotifs(notif); } catch (e) { console.log(e); } }, []); // 依赖改为空数组,只在组件挂载时请求一次 useEffect(() => { Lists(); }, [Lists]); return ( <Box> {notifs && notifs.map((notif) => { return ( <Box key={notif.id}> <ReadMore headerLimit={112} messageLimit={298} isRead={notif.isRead} headerNotif={notif.header} messageNotif={notif.message} /> </Box> ); })} </Box> ); }; export default Notifications;
然后重写ReadMore.js的逻辑
去掉存JSX到状态的写法,直接在渲染时计算显示内容,规避异步状态更新的时序问题,修改后代码:
import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Box, Typography } from '@material-ui/core'; const ReadMore = (props) => { const { headerNotif, messageNotif, isRead, headerLimit, messageLimit } = props; const [isExpanded, setIsExpanded] = useState(false); // 直接计算要显示的头部内容 const displayHeader = isExpanded ? headerNotif : headerNotif.length > headerLimit ? `${headerNotif.substr(0, headerLimit)}...` : headerNotif; // 直接计算要显示的消息内容 const displayMessage = isExpanded ? messageNotif : messageNotif.length > messageLimit ? `${messageNotif.substr(0, messageLimit)}...` : messageNotif; // 判断是否需要显示展开/收起按钮 const showToggle = headerNotif.length > headerLimit || messageNotif.length > messageLimit; return ( <span> <Box sx={{ pb: 1 }}> <Typography variant="subtitle2" style={{ fontSize: 16, color: isRead ? '#333333' : '#008C44', }} > {displayHeader} </Typography> </Box> <Box sx={{ pb: 1 }}> <Typography variant="body2" style={{ fontSize: 12, color: '#666666', }} > {displayMessage} </Typography> </Box> {showToggle && ( <span style={{ color: '#008C44', cursor: 'pointer', display: 'block', fontSize: 14, paddingTop: isExpanded ? 0 : 2 }} onClick={() => setIsExpanded(!isExpanded)} > <u>{isExpanded ? ' View Less' : ' View More'}</u> </span> )} </span> ); }; ReadMore.propTypes = { headerLimit: PropTypes.number, messageLimit: PropTypes.number, headerNotif: PropTypes.string.isRequired, messageNotif: PropTypes.string.isRequired, isRead: PropTypes.bool.isRequired }; export default ReadMore;
优化说明
- 去掉了所有存储JSX的冗余状态,只用一个
isExpanded状态控制展开收起,逻辑更简洁,不会出现异步更新的时序问题 - 修复了原代码里长度等于限制值时的边界判断遗漏问题
- 补全了PropTypes的必填项校验,避免后续参数传错的问题
- 去掉了不必要的状态和函数调用,渲染性能更好
内容的提问来源于stack exchange,提问作者Ricky
相关产品推荐
相关产品推荐

