React组件点击标签按钮后无意义重渲染问题求助
问题:点击随机生成的标签按钮后组件异常重新渲染
我尝试从数据源渲染标签并将其随机生成为按钮,但点击按钮后组件会无理由重新渲染,请问该如何解决?
const Blog = () => { const [articles, setArticles] = useState([]); const [tags, setTags] = useState([]); // 从数据库获取数据 const getData = async () => { try { // 获取文章和标签数据 setArticles([ { name: "title1", id_tag: 1 }, { name: "title2", id_tag: 2 } ]); setTags([ { id: 1, name: "bell" }, { id: 2, name: "fashion" } ]); } catch (e) { console.log(e); } }; useEffect(() => { getData(); }, []); const getRandomTag = (i) => { const newTags = tags; const tag = newTags[Math.floor(Math.random() * newTags.length)]; return ( <button key={i} onClick={() => filterByTag(tag.id)}> {tag.name} </button> ); }; // 根据标签筛选文章 const filterByTag = (id) => { setArticles(articles.filter((article) => article.id_tag === id)); }; return ( <> <ul> {articles.map((article, i) => ( <li key={i}>{article.name}</li> ))} </ul> <h4>TAGS</h4> <ul>{tags.map((tag, i) => getRandomTag(i))}</ul> </> ); }; export default Blog;
解决方案
问题根源
- 每次渲染都重新生成随机按钮:
getRandomTag在组件渲染阶段执行,每次组件状态更新(比如点击按钮修改articles)都会重新调用这个函数,生成全新的随机标签按钮,造成“组件无理由重渲染”的错觉,同时React会因为按钮内容变化而重新渲染这些元素。 - 使用索引作为key:用
tags.map的索引作为按钮的key,当按钮内容随机变化时,React无法正确识别组件的身份,会强制销毁并重建按钮组件,加重不必要的渲染。 - 状态更新的闭包问题:
filterByTag中直接使用当前articles状态,当组件多次渲染后,可能因为闭包捕获到旧的状态值,导致筛选结果不符合预期。 - 原始数据丢失问题:原代码中筛选后直接覆盖
articles状态,无法恢复完整的文章列表,属于隐性缺陷。
修复步骤
- 缓存随机标签按钮列表:使用
useMemo缓存生成的随机按钮,只有当tags变化时才重新生成,避免每次渲染都重新随机。 - 使用唯一标识作为key:用标签的
id结合索引作为按钮的key,让React能正确追踪组件身份。 - 函数式更新状态:修改
filterByTag,使用函数式更新获取最新的articles状态,避免闭包问题。 - 保存原始文章数据:新增状态存储原始文章列表,确保筛选后仍能基于完整数据操作。
修改后的代码
import { useState, useEffect, useMemo } from 'react'; const Blog = () => { const [articles, setArticles] = useState([]); const [tags, setTags] = useState([]); // 保存原始文章数据,避免筛选后丢失全部数据 const [originalArticles, setOriginalArticles] = useState([]); // 从数据库获取数据 const getData = async () => { try { const articleData = [ { name: "title1", id_tag: 1 }, { name: "title2", id_tag: 2 } ]; const tagData = [ { id: 1, name: "bell" }, { id: 2, name: "fashion" } ]; setArticles(articleData); setOriginalArticles(articleData); setTags(tagData); } catch (e) { console.log(e); } }; useEffect(() => { getData(); }, []); // 用useMemo缓存随机按钮列表,仅当tags变化时重新生成 const randomTagButtons = useMemo(() => { return tags.map((_, index) => { const randomTag = tags[Math.floor(Math.random() * tags.length)]; return ( <button key={`${randomTag.id}-${index}`} onClick={() => filterByTag(randomTag.id)}> {randomTag.name} </button> ); }); }, [tags]); // 根据标签筛选文章,使用函数式更新获取最新状态 const filterByTag = (id) => { setArticles(prevArticles => originalArticles.filter(article => article.id_tag === id)); }; return ( <> <ul> {articles.map((article) => ( <li key={article.name}>{article.name}</li> ))} </ul> <h4>TAGS</h4> <ul>{randomTagButtons}</ul> </> ); }; export default Blog;
内容的提问来源于stack exchange,提问作者Swiler
相关产品推荐
相关产品推荐

