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

React自定义Hook疑问:为何仅Test组件触发重渲染?

问题原因分析

核心点:React自定义Hook是复用状态逻辑的工具,而非共享状态的容器。每个组件调用自定义Hook时,Hook内部的useState都会生成完全独立的状态实例,不同组件的状态相互隔离。

  • 当Test组件调用useTest()时,Hook里的const [test, setTest] = useState(...)会初始化一套属于Test的专属状态;
  • 当Test2组件调用useTest()时,同样会初始化另一套完全独立的test状态;
  • 你点击Test的按钮,更新的只是Test组件自己那套test状态,Test2的test还是初始的["orange", "apple", "banana"],没有发生变化,所以Test2不会触发重渲染。
实现组件间共享状态的方案

如果想让Test和Test2共享同一套test状态,有两种常用实现方式:

1. 状态提升到共同父组件(App)

把状态和逻辑移到App组件,通过props传递给子组件:

// App.tsx
import React, { useState } from 'react';
import { Test } from './Test';
import { Test2 } from './Test2';

function App() {
  const [test, setTest] = useState(["orange", "apple", "banana"]);
  const [count, setCount] = useState(0);
  const testSet = new Set(test);

  const increment = () => {
    setCount(count + 1);
    setTest(pre => [...pre, `grape${count}`]);
  };

  return (
    <>
      <Test test={test} testSet={testSet} increment={increment} />
      <Test2 test={test} testSet={testSet} />
    </>
  );
}

export default App;

修改子组件接收props:

// Test.tsx
export const Test = ({ test, testSet, increment }) => {
  console.log("Test1 is rendered");

  return (
    <div>
      <h1>Test1</h1>
      <h2>{test.join(', ')}</h2>
      <h2>{Array.from(testSet).join(', ')}</h2>
      <button onClick={increment}>Click me</button>
    </div>
  );
}
// Test2.tsx
export const Test2 = ({ test }) => {
  console.log("Test2 is rendered");

  return (
    <div>
      <h1>Test2</h1>
      <h2>{test.join(', ')}</h2>
    </div>
  );
}

2. 使用Context API(适合跨多层组件共享)

如果组件层级较深,props传递过于繁琐,可以用Context统一管理共享状态:

首先创建Context和Provider:

// hooks/TestContext.tsx
import React, { createContext, useContext, useState, useMemo } from 'react';

interface TestContextType {
  test: string[];
  testSet: Set<string>;
  increment: () => void;
}

const TestContext = createContext<TestContextType | undefined>(undefined);

export const TestProvider = ({ children }: { children: React.ReactNode }) => {
  const [test, setTest] = useState(["orange", "apple", "banana"]);
  const [count, setCount] = useState(0);
  // 用useMemo避免每次渲染都创建新Set
  const testSet = useMemo(() => new Set(test), [test]);

  const increment = () => {
    setCount(count + 1);
    setTest(pre => [...pre, `grape${count}`]);
  };

  const value = useMemo(() => ({ test, testSet, increment }), [test, testSet, increment]);

  return <TestContext.Provider value={value}>{children}</TestContext.Provider>;
};

export const useTestContext = () => {
  const context = useContext(TestContext);
  if (!context) {
    throw new Error('useTestContext must be used within a TestProvider');
  }
  return context;
};

在App中包裹Provider:

// App.tsx
import React from 'react';
import { Test } from './Test';
import { Test2 } from './Test2';
import { TestProvider } from './hooks/TestContext';

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

export default App;

子组件通过Context Hook获取状态:

// Test.tsx
import { useTestContext } from "./hooks/TestContext";

export const Test = () => {
  console.log("Test1 is rendered");
  const { test, testSet, increment } = useTestContext();

  return (
    <div>
      <h1>Test1</h1>
      <h2>{test.join(', ')}</h2>
      <h2>{Array.from(testSet).join(', ')}</h2>
      <button onClick={increment}>Click me</button>
    </div>
  );
}
// Test2.tsx
import { useTestContext } from "./hooks/TestContext";

export const Test2 = () => {
  console.log("Test2 is rendered");
  const { test } = useTestContext();

  return (
    <div>
      <h1>Test2</h1>
      <h2>{test.join(', ')}</h2>
    </div>
  );
}

修改后,点击Test组件的按钮,两个组件都会因共享状态更新触发重渲染。


内容的提问来源于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:26:21