You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

解决方案

问题根源

  1. 每次渲染都重新生成随机按钮:getRandomTag在组件渲染阶段执行,每次组件状态更新(比如点击按钮修改articles)都会重新调用这个函数,生成全新的随机标签按钮,造成“组件无理由重渲染”的错觉,同时React会因为按钮内容变化而重新渲染这些元素。
  2. 使用索引作为key:用tags.map的索引作为按钮的key,当按钮内容随机变化时,React无法正确识别组件的身份,会强制销毁并重建按钮组件,加重不必要的渲染。
  3. 状态更新的闭包问题:filterByTag中直接使用当前articles状态,当组件多次渲染后,可能因为闭包捕获到旧的状态值,导致筛选结果不符合预期。
  4. 原始数据丢失问题:原代码中筛选后直接覆盖articles状态,无法恢复完整的文章列表,属于隐性缺陷。

修复步骤

  1. 缓存随机标签按钮列表:使用useMemo缓存生成的随机按钮,只有当tags变化时才重新生成,避免每次渲染都重新随机。
  2. 使用唯一标识作为key:用标签的id结合索引作为按钮的key,让React能正确追踪组件身份。
  3. 函数式更新状态:修改filterByTag,使用函数式更新获取最新的articles状态,避免闭包问题。
  4. 保存原始文章数据:新增状态存储原始文章列表,确保筛选后仍能基于完整数据操作。

修改后的代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:45:27