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

React中new Set(array)重渲染时未正常更新的问题咨询

问题描述

点击“Click me”按钮时,test数组能正确更新,但testSet集合却无法正常更新——确切地说,testSet仅更新一次,且长度始终不超过5。

相关代码

useTest.ts

import { useState } from "react";

export const useTest = () => {
  const [test, setTest] = useState(["orange", "apple", "banana"]);
  const testSet = new Set(test);
  console.log(test)
  console.log(testSet)

  const increment = () => {
    test.push("grape");
    setTest([...test]);
    // setTest(newTest);
  };

  return { test, testSet, increment}
}

App.tsx

import React from 'react';
import { Test } from './Test';
// import { Test2 } from './Test2';

function App() {
  return (
    <>
      <Test />
      {/* <Test2 /> */}
    </>
  );
}

export default App;

Test.tsx

import { useTest } from "./hooks/useTest";

export const Test = () => {
  const {test, testSet, increment} = useTest();

  return (
    <div>
      <h1>Test1</h1>
      <h2>{test}</h2>
      <h2>{testSet}</h2>
      <button onClick={() => {increment()}}>Click me</button>
    </div>
  );
}

问题原因

  1. 违反State不可变原则:increment函数中直接调用test.push("grape")修改原state数组,虽然后续用setTest([...test])生成新数组,但这种操作会导致状态更新逻辑混乱,不符合React设计规范。
  2. Set自动去重特性:每次添加的都是固定值"grape",Set集合仅保留唯一值,第一次添加后后续的"grape"不会被加入,因此集合大小停留在4(初始3个元素+1个新元素),并非“不超过5”。
  3. 渲染方式错误:JSX中直接渲染testSet会显示[object Set],无法直观看到集合内容,容易误判集合未更新。

解决方法

1. 正确更新State数组

避免直接修改原数组,基于前一次state创建新数组:

const increment = () => {
  // 用时间戳生成唯一值,测试集合的持续更新
  setTest(prevTest => [...prevTest, `grape-${Date.now()}`]);
};

2. 用useMemo缓存派生的Set集合

使用useMemo缓存testSet,确保仅在test数组变化时重新创建集合:

import { useState, useMemo } from "react";

export const useTest = () => {
  const [test, setTest] = useState(["orange", "apple", "banana"]);
  
  // 依赖test数组,仅当test变化时重新生成Set
  const testSet = useMemo(() => new Set(test), [test]);

  console.log(test);
  console.log(testSet);

  const increment = () => {
    setTest(prev => [...prev, `grape-${Date.now()}`]);
  };

  return { test, testSet, increment };
};

3. 正确渲染集合内容

将Set转换为数组后再渲染,才能看到实际元素:

export const Test = () => {
  const { test, testSet, increment } = useTest();

  return (
    <div>
      <h1>Test1</h1>
      <h2>{test.join(', ')}</h2>
      {/* 转换Set为数组后显示 */}
      <h2>{Array.from(testSet).join(', ')}</h2>
      <button onClick={increment}>Click me</button>
    </div>
  );
};

内容的提问来源于stack exchange,提问作者Nanano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:16:00