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

React useMemo监听数组依赖失效问题求助(TypeScript)

问题分析与解决方案

核心问题

React组件无法自动感知外部类实例的属性变化——组件的重新渲染仅由自身状态(state)、传入属性(props)或上下文(context)的变化触发。你的FetchingService是单例对象,它的datas属性更新时,不会通知React组件重新渲染,因此useMemo的依赖检查永远不会被触发(组件没重新渲染,useMemo不会重新执行)。另外,useMemo的设计目的是缓存昂贵计算结果,不是用来监听外部变化并更新状态,这里属于用法错误。


解决方案1:给服务类添加订阅机制

让FetchingService支持订阅,当datas更新时主动通知所有订阅的组件,这是全局状态与React组件联动的常规方式:

修改服务类:

export default class FetchingService {
    datas: Data[] = [];
    private subscribers: ((data: Data[]) => void)[] = [];

    constructor() {
        this.fetch();
    }

    async fetch(): Promise<Data[]> {        
        // 替换为你的API请求逻辑
        const d = await fetch('/api/your-data')
            .then(res => res.json())
            .then(rawData => rawData.flat());
        
        this.datas = d;
        this.notifySubscribers(d);
        console.log(this.datas);
        return d;
    }

    // 订阅数据变化
    subscribe(callback: (data: Data[]) => void) {
        this.subscribers.push(callback);
        // 订阅时立即返回当前数据
        callback(this.datas);
        // 返回取消订阅的函数,避免内存泄漏
        return () => {
            this.subscribers = this.subscribers.filter(cb => cb !== callback);
        };
    }

    // 通知所有订阅者数据更新
    private notifySubscribers(data: Data[]) {
        this.subscribers.forEach(cb => cb(data));
    }
}

修改组件:

import fetchingService from '../services/fetchingService.ts';

const Home: React.FC = () => {
  const [datas, setDatas] = useState<Data[]>([]);

  useEffect(() => {
    // 订阅服务类的数据变化,更新组件状态
    const unsubscribe = fetchingService.subscribe(setDatas);
    // 组件卸载时取消订阅
    return unsubscribe;
  }, []);

  return (
    <ul>{datas.map(d => (
      <li key={d.id}>{d.id}</li>
    ))}</ul>
  );
}

解决方案2:直接在组件内发起请求(更符合React常规实践)

如果你的数据不需要全局共享,没必要用单例类,直接把服务改成工具函数,在组件内用useEffect发起请求:

修改服务为工具函数:

export const fetchDatas = async (): Promise<Data[]> => {        
    const d = await fetch('/api/your-data')
        .then(res => res.json())
        .then(rawData => rawData.flat());
    console.log(d);
    return d;
};

修改组件:

import { fetchDatas } from '../services/fetchingService.ts';

const Home: React.FC = () => {
  const [datas, setDatas] = useState<Data[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const getDatas = async () => {
      const data = await fetchDatas();
      setDatas(data);
      setLoading(false);
    };
    getDatas();
  }, []);

  // 加载状态提示
  if (loading) return <div>加载中...</div>;

  return (
    <ul>{datas.map(d => (
      <li key={d.id}>{d.id}</li>
    ))}</ul>
  );
}

补充说明

  1. 不要用useMemo更新状态:它的作用是缓存重复计算的结果,比如复杂数组过滤、转换,不是用来监听外部变化的。
  2. React的依赖检查逻辑:只有组件重新渲染时,才会检查useMemo/useEffect的依赖是否变化,外部对象的属性更新不会触发组件渲染。
  3. 如果需要全局共享状态,也可以用React Context搭配useReducer来管理,比自定义订阅机制更贴合React生态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:15:44