React使用axios获取用户数据后topbar重复渲染问题
问题描述
React项目中预期仅渲染1个topbar组件,接入axios获取用户头像、增加.map()遍历逻辑后,topbar出现重复渲染,问题效果截图:
问题相关原始代码:
import "./topbar.css" import { Search } from '@mui/icons-material' import { useState, useEffect, Fragment } from 'react' import axios from "axios" function Home() { const [user, setPosts] = useState([]) useEffect(() => { console.log("chargement ok") const fetchData = async () => { const result = await axios.get( 'http://localhost:4200/api/user/') setPosts(result.data) } fetchData(); }, []) return ( <Fragment> { user ? user.map((users,topbar) => ( <div key={topbar} className="topBarContainer"> <div className="topBarLeft"> <span className="logo">Groupomania</span> </div> <div className="topBarCenter"> <div className="searchBar"> <Search className="searchIcon" /> <input placeholder="Vous cherchez quelque chose ?" className="searchInput" /> </div> </div> <div className="topBarRight"> <div className="topBarLinks"> <span className="topBarLink">Page d'acceuil</span> <span className="topBarLink">Deconnexion</span> </div> <img src={users.picture} alt="Photo de profil de l'utilisateur" className="topBarImg" /> </div> </div>)) : (<p></p>) } </Fragment> ) } export default Home
问题根因
重复渲染的核心原因是遍历逻辑写错了位置:
- 你把整个topbar容器的DOM结构完全放在了
user.map()的遍历回调内部 - 调用的
/api/user/接口返回的是全量用户列表数组,Array.map()会为数组里的每一个元素都执行一次回调、返回一份对应的DOM结构 - 接口返回多少条用户数据,页面就会渲染多少个topbar,直接导致重复展示问题
- 附带的代码问题:state命名语义混乱(
const [user, setPosts] = useState([])变量名和setter名不匹配),且空数组为truthy值,现有三目判断的空态逻辑实际不会生效;列表key用数组下标也不符合React最佳实践,容易出现渲染异常。
修复方案
固定的topbar结构只需要写1份放在遍历逻辑外层,仅动态填充需要的用户头像字段即可,不要把公共固定组件包裹在列表遍历里。参考修正后代码:
import "./topbar.css" import { Search } from '@mui/icons-material' import { useState, useEffect, Fragment } from 'react' import axios from "axios" function Home() { // 修正命名语义,单独存储当前登录用户信息 const [currentUser, setCurrentUser] = useState(null) useEffect(() => { const fetchData = async () => { const result = await axios.get( // 建议替换为获取「当前登录用户」的单独接口,不要拉取全量用户列表 'http://localhost:4200/api/user/current' ) setCurrentUser(result.data) } fetchData(); }, []) return ( <Fragment> {/* 整个topbar固定结构只渲染1次 */} <div className="topBarContainer"> <div className="topBarLeft"> <span className="logo">Groupomania</span> </div> <div className="topBarCenter"> <div className="searchBar"> <Search className="searchIcon" /> <input placeholder="Vous cherchez quelque chose ?" className="searchInput" /> </div> </div> <div className="topBarRight"> <div className="topBarLinks"> <span className="topBarLink">Page d'acceuil</span> <span className="topBarLink">Deconnexion</span> </div> {/* 仅头像部分取动态用户数据,加空值兜底 */} {currentUser && <img src={currentUser.picture} alt="Photo de profil de l'utilisateur" className="topBarImg" />} </div> </div> </Fragment> ) } export default Home
如果暂时没有单独获取当前登录用户的接口,临时方案可以先取用户数组的第一项渲染头像,但不建议生产环境这么做——拉取全量用户数据仅用来展示当前登录人头像属于明显的接口性能浪费。
如果业务确实需要渲染全量用户列表,请把用户列表的遍历逻辑放到topbar外面对应的内容区域,不要包裹导航类公共固定组件。
内容的提问来源于stack exchange,提问作者Tewho
相关产品推荐
相关产品推荐

