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> ); }
问题原因
- 违反State不可变原则:
increment函数中直接调用test.push("grape")修改原state数组,虽然后续用setTest([...test])生成新数组,但这种操作会导致状态更新逻辑混乱,不符合React设计规范。 - Set自动去重特性:每次添加的都是固定值
"grape",Set集合仅保留唯一值,第一次添加后后续的"grape"不会被加入,因此集合大小停留在4(初始3个元素+1个新元素),并非“不超过5”。 - 渲染方式错误: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
相关产品推荐
相关产品推荐

