React 派生状态计算:利用代理模式(Proxy)实现基于原始状态的高性能自动计算逻辑

React 派生状态计算:利用代理模式(Proxy)实现基于原始状态的高性能自动计算逻辑

欢迎来到今天的讲座。我是你们的老朋友,一个在 React 代码堆里摸爬滚打多年,头发日渐稀疏但依然热爱技术的前端工程师。

今天我们要聊一个稍微有点“反直觉”,但绝对能让你在代码评审时让同事眼前一亮(或者吓他们一跳)的话题。我们将深入探讨 React 派生状态计算,并使用一个古老而强大的 JavaScript 特性——代理模式——来彻底解放我们的 useEffect

准备好了吗?我们要开始“变形”了。


第一部分:派生状态的地狱与“手动”的痛苦

首先,让我们看看我们每天都在做什么。

假设你正在开发一个电商应用。你有一个购物车组件。这个购物车里有什么?有商品列表,有数量,有单价,有折扣码,还有总价。

在传统的 React 模式下,通常是这样的:

import React, { useState, useEffect, useMemo } from 'react';

const CartComponent = ({ items }) => {
  // 1. 原始状态:存什么存什么,存得乱七八糟
  const [cartState, setCartState] = useState({
    items: items,
    discount: 0,
    taxRate: 0.1
  });

  // 2. 派生状态:你需要手动计算
  // 为了让总价随折扣变化,你需要手动监听
  const [totalPrice, setTotalPrice] = useState(0);
  const [finalTotal, setFinalTotal] = useState(0);

  // 3. 搞定同步:这简直是噩梦
  useEffect(() => {
    // 计算原始总价
    const rawTotal = cartState.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    setTotalPrice(rawTotal);

    // 计算含税总价
    const taxed = rawTotal * (1 + cartState.taxRate);
    // 应用折扣
    const final = taxed * (1 - cartState.discount);
    setFinalTotal(final);
  }, [cartState.items, cartState.discount, cartState.taxRate]); // 依赖项长长一串

  // 4. 修改状态时,还得手动同步
  const updateQuantity = (id, newQty) => {
    const newItems = cartState.items.map(item => 
      item.id === id ? { ...item, quantity: newQty } : item
    );
    setCartState({ ...cartState, items: newItems });
    // 注意:这里我们手动触发了 useEffect,但副作用逻辑其实写在 useEffect 里了
    // 如果逻辑复杂,你可能会写一个单独的函数来处理计算,然后在 useEffect 里调用它
  };

  const applyDiscount = (code) => {
    // 假设逻辑...
    setCartState({ ...cartState, discount: 0.2 });
  };

  return (
    <div>
      <h1>购物车</h1>
      <ul>
        {cartState.items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price} x {item.quantity}
            <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>
          </li>
        ))}
      </ul>
      <div>原始总价: ${totalPrice.toFixed(2)}</div>
      <div>最终总价: ${finalTotal.toFixed(2)}</div>
    </div>
  );
};

看,这就是“屎山”的雏形。

  1. 数据不一致风险totalPricefinalTotal 存在组件的 state 里。虽然它们是计算出来的,但它们也是“真实”存在的。这意味着如果你不小心在别处直接修改了 totalPrice,或者数据流断了,你的界面就会显示错误。
  2. 样板代码爆炸:每次计算一个新字段,你都要写一个 useState,写一个 useEffect,还要在 useEffect 的依赖数组里把所有用到的父级状态加进去。如果父级状态嵌套很深,依赖数组就会变成一个长长的列表,让人看着眼晕。
  3. 性能隐患:虽然我们用了 useMemo,但手动管理依赖依然容易出错。有时候你觉得依赖了 items 就够了,结果因为 items 是一个对象引用,导致不必要的重新渲染。

我们的目标很简单: 我们希望保留 React 的声明式思维,但获得命令式编程的“即时性”。我们希望当 cartState.items 变化时,totalPricefinalTotal 能像变魔术一样自动更新,不需要我们手动去 setTotalPrice

这听起来像魔法?不,这听起来像 响应式编程

而实现响应式编程,在 JavaScript 中,最古老的黑魔法工具就是 Proxy


第二部分:Proxy —— 代码世界的“特务”

在 ES6 之前,如果你想要拦截一个对象的操作,那得写一大堆 getter 和 setter,或者使用 Object.defineProperty。那玩意儿对于数组索引的监听简直是噩梦。

然后 ES6 出现了 Proxy

什么是 Proxy?

你可以把 Proxy 理解为一个“特务”或者“间谍”

想象一下,你有一个对象 target,它是你真实的业务数据。你不想直接碰它,你想在别人碰它之前,先看看他在干什么,甚至阻止他,或者在他碰完之后做点手脚。

const target = {
  name: 'Alice',
  age: 30
};

const handler = {
  get(target, prop) {
    console.log(`有人想读取 ${prop} 属性`);
    return target[prop]; // 允许读取
  },
  set(target, prop, value) {
    console.log(`有人想修改 ${prop} 为 ${value}`);
    target[prop] = value; // 执行修改
    return true; // 告诉世界修改成功
  }
};

const proxy = new Proxy(target, handler);

// 现在,当我们操作 proxy 时,handler 会先执行
proxy.name = 'Bob'; // 输出:有人想修改 name 为 Bob
console.log(proxy.name); // 输出:有人想读取 name 属性,然后返回 Bob

这个 handler(处理程序)就是我们的核心。它有两个最重要的方法:

  1. get(target, prop, receiver): 当有人读取属性时触发。
  2. set(target, prop, value, receiver): 当有人写入属性时触发。

我们的策略是:利用 set 修改原始数据,利用 get 返回计算后的派生数据。


第三部分:构建“自动计算”的核心引擎

让我们来写一个函数,这个函数能把普通的对象变成一个“自动计算”的智能对象。

我们需要一个工厂函数,它接受两个参数:

  1. initialState: 初始数据。
  2. derivations: 一个函数,定义如何从原始数据计算出派生数据。
function createDerivedState(initialState, derivations) {
  // 1. 创建一个“原始数据”的副本,或者就是它本身
  // 为了安全起见,我们深拷贝一下,防止外部直接修改导致 Proxy 失效
  let target = JSON.parse(JSON.stringify(initialState));

  // 2. 创建 Proxy
  const proxy = new Proxy(target, {
    // 拦截读取操作
    get(target, prop) {
      // 如果这个属性是 derivations 定义的派生字段
      if (derivations[prop]) {
        // 执行计算,并返回结果
        return derivations[prop](target);
      }

      // 如果不是派生字段,就返回原始值
      return target[prop];
    },

    // 拦截写入操作
    set(target, prop, value) {
      // 先更新原始数据
      target[prop] = value;

      // 关键点:这里我们不返回 true,而是返回一个对象,包含 computed 属性
      // 这样我们就能在 React 里区分“数据变了”和“计算值变了”
      return {
        computed: true,
        value: derivations[prop] ? derivations[prop](target) : value
      };
    }
  });

  return proxy;
}

等等,这还不够完美。 上述代码有个问题:JSON.parse(JSON.stringify()) 只能处理简单的 JSON 对象。如果我们的状态里有函数、循环引用或者特殊的对象,它会崩掉。而且,它不能处理嵌套对象的派生。

让我们把它升级一下,支持嵌套对象,并使用 Reflect 来保持代码的纯粹性。

function createDerivedState(initialState, derivations) {
  // 我们不深拷贝,而是创建一个代理对象作为“容器”
  // 这样我们可以拦截嵌套对象的访问
  const container = {
    data: initialState
  };

  const handler = {
    get(target, prop) {
      // 如果 prop 是 derivations 里的函数(派生属性)
      if (typeof derivations[prop] === 'function') {
        // 我们返回一个 getter 函数,这样每次访问时都会重新计算
        return () => derivations[prop](container.data);
      }

      // 如果 prop 是 'data',返回原始数据
      if (prop === 'data') {
        return container.data;
      }

      // 否则,返回原始数据中的属性
      return Reflect.get(container.data, prop);
    },

    set(target, prop, value) {
      // 如果 prop 是 derivations 里的函数,说明试图给派生属性赋值(非法操作)
      if (typeof derivations[prop] === 'function') {
        console.warn(`Cannot set derived state property: ${prop}`);
        return false;
      }

      // 更新原始数据
      Reflect.set(container.data, prop, value);

      // 返回 true 表示成功
      return true;
    }
  };

  return new Proxy(container, handler);
}

这个版本的逻辑是:

  • 我们创建了一个 container 对象,里面放着真实的 data
  • Proxy 监听 container
  • 当你访问 proxy.foo 时:
    • 如果 derivations 里定义了 foo,它返回一个函数 () => derivations.foo(data)。当你调用这个函数时,才会触发计算。
    • 如果没有定义,它返回 data.foo

第四部分:在 React 中拥抱 Proxy —— useProxyState Hook

现在,我们有了引擎,怎么把它塞进 React 里?

React 的核心在于渲染。我们需要在数据变化时触发渲染。

import React, { useState, useEffect, useRef } from 'react';

function useProxyState(initialState, derivations) {
  // 1. 创建 Proxy 实例
  const proxy = useRef(createDerivedState(initialState, derivations)).current;

  // 2. 创建一个 state 来存储原始数据
  // 为什么不直接用 proxy 作为 state?因为 Proxy 是不可变的引用(虽然内部可变),直接作为 state 会导致不必要的重渲染
  // 我们需要一个“脏”标记来触发渲染
  const [, setRenderKey] = useState(0);

  // 3. 监听原始数据的变化
  useEffect(() => {
    // 我们需要一个函数来检查数据是否真的变了
    // 因为 Proxy 的 get 返回的是计算值,我们需要对比原始数据
    const checkChanges = () => {
      // 这里有个难点:如何检测深层对象的变化?
      // 简单的做法是每次 setRenderKey,但这样性能不好。
      // 更好的做法是使用 JSON.stringify 或者 lodash.isEqual。
      // 但为了演示 Proxy 的威力,我们假设每次写入都会触发。

      // 在实际生产中,你可能会使用一个 Proxy 的 set handler 来手动触发 setRenderKey
      // 但为了代码清晰,我们这里简化处理:
      // 我们不在这里做检查,而是把触发渲染的逻辑放在 Proxy 的 set handler 里
    };

    // 注册一个全局监听器(这里简化,实际可以用更复杂的状态管理库模式)
    // 注意:这只是一个概念演示,真实的 React 应用中,通常不会在组件外部做全局监听
    // 我们将在下面重写 Proxy 的 set handler 来解决这个问题。
  }, []);

  // 重写 Proxy 的逻辑,加入渲染触发
  // 这是一个稍微高级一点的实现,结合了 Proxy 和 React 的生命周期
  const renderProxy = createDerivedState(initialState, derivations);

  // 我们需要一个 ref 来保存当前的 renderProxy
  const proxyRef = useRef(renderProxy);

  // 修改 Proxy 的 set handler,使其在数据变更时触发 React 渲染
  const enhancedHandler = {
    get(target, prop) {
      if (typeof derivations[prop] === 'function') {
        return () => derivations[prop](proxyRef.current.data);
      }
      if (prop === 'data') return proxyRef.current.data;
      return Reflect.get(proxyRef.current.data, prop);
    },
    set(target, prop, value) {
      // 更新原始数据
      Reflect.set(proxyRef.current.data, prop, value);

      // 触发 React 渲染
      // 这里我们稍微 hack 一下,通过改变 state 的 key 来强制重绘
      // 在实际项目中,你应该使用 useMemo 或其他机制来优化
      // 但为了演示“自动计算”,这最直观
      // proxyRef.current = createDerivedState(proxyRef.current.data, derivations); // 这种方式会丢失引用

      // 正确的做法是:不要在 set handler 里直接调用 setState,
      // 而是让 React 的 useEffect 监听 proxyRef.current.data 的变化
      // 但 Proxy 的 get 返回的是新值,这会导致 useEffect 无限循环。

      // 妥协方案:使用一个“脏”计数器或者直接修改 state 的 key
      // 这里为了演示方便,我们采用一种“手动触发”的模式
      // 在组件内部,我们提供一个 set 方法,而不是直接暴露 proxy
      return true;
    }
  };

  // ... (这部分逻辑比较复杂,我们换一种更优雅的写法,使用 useSyncExternalStore 或者简单的 useEffect)

  // 重新定义:我们使用一个辅助函数来生成带渲染能力的 Proxy
  const createReactiveProxy = (initial, derivations) => {
    let data = initial;
    const listeners = new Set();

    const handler = {
      get(target, prop) {
        // 如果是派生属性
        if (derivations[prop]) {
          return derivations[prop](data);
        }
        return Reflect.get(data, prop);
      },
      set(target, prop, value) {
        const oldValue = data[prop];
        if (oldValue !== value) {
          data[prop] = value;
          // 通知所有监听者(这里简化为强制重渲染)
          listeners.forEach(fn => fn());
        }
        return true;
      }
    };

    const proxy = new Proxy({}, handler);

    return {
      proxy,
      subscribe: (fn) => {
        listeners.add(fn);
        return () => listeners.delete(fn);
      },
      get data() { return data; }
    };
  };

  const { proxy, subscribe } = createReactiveProxy(initialState, derivations);

  useEffect(() => {
    const unsubscribe = subscribe(() => {
      // 当数据变化时,更新组件的 key
      setRenderKey(prev => prev + 1);
    });
    return unsubscribe;
  }, [derivations]); // derivations 变了也要重订阅

  return proxy;
}

太好了,这才是我们要的 Hook!

现在,让我们把这个 Hook 用回之前的购物车案例。

const CartComponent = ({ items }) => {
  // 1. 定义初始状态
  const initialState = {
    discount: 0,
    taxRate: 0.1,
    items: items // 注意:这里 items 是从 props 传进来的
  };

  // 2. 定义派生逻辑
  // 所有的计算逻辑都集中在这里,清晰明了
  const derivations = {
    // 计算总价
    rawTotal: (state) => state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),

    // 计算含税总价
    totalWithTax: (state) => {
      const raw = derivations.rawTotal(state);
      return raw * (1 + state.taxRate);
    },

    // 计算最终总价
    finalTotal: (state) => {
      return derivations.totalWithTax(state) * (1 - state.discount);
    }
  };

  // 3. 获取 Proxy 实例
  const proxy = useProxyState(initialState, derivations);

  // 4. 修改数据的方法
  const updateQuantity = (id, newQty) => {
    const newItems = proxy.data.items.map(item => 
      item.id === id ? { ...item, quantity: newQty } : item
    );
    // 直接修改原始数据,Proxy 会自动拦截并触发重渲染
    proxy.data.items = newItems; 
  };

  const applyDiscount = (code) => {
    // 直接修改
    proxy.data.discount = 0.2;
  };

  // 5. 渲染
  return (
    <div>
      <h1>智能购物车</h1>
      <ul>
        {proxy.data.items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price} x {item.quantity}
            <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>
          </li>
        ))}
      </ul>

      {/* 计算结果自动显示,无需手动 useState */}
      <div>原始总价: ${proxy.rawTotal.toFixed(2)}</div>
      <div>含税总价: ${proxy.totalWithTax.toFixed(2)}</div>
      <div>最终总价: ${proxy.finalTotal.toFixed(2)}</div>

      <button onClick={() => applyDiscount('SAVE20')}>使用折扣码</button>
    </div>
  );
};

看! 代码行数从 40 多行变成了 30 行左右,而且逻辑更清晰了。最重要的是,我们没有 useEffect。没有手动同步 totalPricefinalTotal。当 discount 变化时,finalTotal 会自动重新计算并更新界面。


第五部分:深入探讨 Proxy 的性能与陷阱

虽然 Proxy 看起来很美,但它不是银弹。作为资深专家,我必须告诉你其中的坑。

1. 性能陷阱:过度计算

Proxy 的 get 方法在每次访问属性时都会执行。

// 假设 derivations.rawTotal 是一个昂贵的计算
const derivations = {
  rawTotal: (state) => {
    console.log('计算发生了!'); // 这行代码会在每次渲染时打印 N 次
    return state.items.reduce(...);
  }
};

// 在 JSX 中
<div>总价: {proxy.rawTotal.toFixed(2)}</div>

如果 rawTotal 计算耗时 50ms,而你的组件渲染了 100 次(比如父组件疯狂重渲染),那么你的页面就会卡死。

解决方案:

  • 缓存结果:不要在 get 里每次都算。你应该在 set 之后缓存结果,或者使用 useMemo 包裹计算逻辑。
  • 手动控制:Proxy 只负责监听变化,计算逻辑应该由你决定何时执行。

让我们优化一下 createDerivedState,加入缓存机制:

function createDerivedState(initialState, derivations) {
  let data = initialState;
  const cache = new Map(); // 缓存计算结果

  const handler = {
    get(target, prop) {
      // 如果是派生属性
      if (derivations[prop]) {
        // 1. 检查缓存
        if (cache.has(prop)) {
          return cache.get(prop);
        }

        // 2. 执行计算
        const result = derivations[prop](data);

        // 3. 存入缓存
        cache.set(prop, result);

        return result;
      }
      return Reflect.get(data, prop);
    },
    set(target, prop, value) {
      const oldValue = data[prop];
      if (oldValue !== value) {
        data[prop] = value;

        // 4. 关键点:数据变了,清空该属性相关的缓存
        // 如果派生属性 A 依赖于 B,当 B 变了,A 必须重算
        // 简单起见,我们清空所有缓存
        cache.clear(); 

        // 通知监听者
        listeners.forEach(fn => fn());
      }
      return true;
    }
  };

  // ... (listeners 逻辑同上)
}

现在,Proxy 会自动处理缓存失效。这非常优雅。

2. 循环依赖的噩梦

这是 Proxy 模式最大的敌人。

假设:
A 依赖于 B
B 依赖于 A

const derivations = {
  a: (state) => state.b + 1,
  b: (state) => state.a + 1
};

当你访问 proxy.a 时,它会调用 derivations.aderivations.a 访问了 state.bstate.b 又调用了 derivations.b……然后 derivations.b 又想访问 state.a……

结果: 栈溢出。

解决方案:

  • 避免循环依赖:这是设计上的问题。你应该重新思考你的状态结构。通常可以通过引入中间状态来打破循环。
  • 深度限制:在计算函数里加一个计数器,超过 100 层就报错。

3. 不可变性的冲突

React 喜欢不可变数据。state.items = newItems 这种写法在 React 原生模式下是被禁止的(或者至少是不推荐的,虽然 Hooks 允许)。

使用 Proxy,我们实际上是在写“可变”的代码,但看起来像“不可变”的代码。这能带来性能提升(避免深拷贝),但也增加了心智负担。你需要时刻记住:Proxy 只是拦截了操作,并没有改变 React 的渲染机制。


第六部分:高级应用场景

Proxy 的威力不仅仅在于购物车。它可以应用在任何需要“响应式”的场景。

场景一:表单验证与格式化

想象一个复杂的搜索表单。

const SearchForm = () => {
  const [proxy] = useProxyState(
    { keyword: '', age: 0, date: null },
    {
      isValid: (state) => state.keyword.length > 3 && state.age > 18,
      formattedDate: (state) => state.date ? state.date.toISOString().split('T')[0] : '',
      hint: (state) => state.isValid ? '搜索成功' : '请输入更多信息'
    }
  );

  return (
    <form>
      <input value={proxy.keyword} onChange={e => proxy.keyword = e.target.value} />
      <input type="number" value={proxy.age} onChange={e => proxy.age = Number(e.target.value)} />

      <div>{proxy.formattedDate}</div>
      <div style={{ color: proxy.isValid ? 'green' : 'red' }}>{proxy.hint}</div>

      <button disabled={!proxy.isValid}>搜索</button>
    </form>
  );
};

在这里,isValidhint 是实时计算的。当你输入字符时,错误提示会立即消失。不需要 useEffect 监听 keyword 的长度。

场景二:仪表盘数据聚合

假设你有 10 个 API 请求,每个返回一个数字。你需要计算平均值、最大值、最小值,以及是否“所有数据都大于 50”。

const Dashboard = () => {
  const [proxy] = useProxyState(
    { temp1: 20, temp2: 30, temp3: 40 },
    {
      avgTemp: (state) => (state.temp1 + state.temp2 + state.temp3) / 3,
      status: (state) => state.avgTemp > 30 ? 'Heat Warning' : 'Normal'
    }
  );

  return <div>温度: {proxy.avgTemp} - 状态: {proxy.status}</div>;
};

当任何一个温度变化,所有派生值都会自动更新。


第七部分:与 React 生态系统的结合

你可能会问:“既然有 MobX 和 Recoil,为什么还要自己写 Proxy?”

这是一个好问题。

  • MobX:本质上也是基于 Proxy 的(早期版本),它提供了一个完整的响应式生态系统,包括 observablecomputedautorun。它更强大,但引入整个库有时候有点杀鸡用牛刀。
  • Recoil:它有自己的状态管理机制,不直接使用 Proxy,而是使用 RecoilValueselector。Selector 本质上就是派生状态,但它依赖于 React 的渲染周期,性能优化比较困难。

我们的 Proxy 方案:

这是一种轻量级的、可定制的方案。它不需要引入庞大的依赖库,你可以完全掌控计算逻辑。它非常适合中小型项目,或者那些不想引入额外状态管理库,但又想摆脱 useEffect 绑架的组件。


第八部分:终极实战 —— 打造一个“自动路由”组件

让我们来个更有趣的例子。假设你正在做一个基于角色的路由系统。

根据用户的 role(角色),你决定显示不同的菜单和按钮。

const App = () => {
  const [user, setUser] = useProxyState(
    { name: 'Alice', role: 'admin', isLoggedIn: true },
    {
      canEdit: (state) => state.role === 'admin' || state.role === 'editor',
      menuItems: (state) => {
        if (state.role === 'admin') return ['Users', 'Settings', 'Logs'];
        if (state.role === 'editor') return ['Articles', 'Media'];
        return ['Home', 'Profile'];
      },
      greeting: (state) => `Hello, ${state.name}. You are a ${state.role}.`
    }
  );

  const handleLogin = () => setUser({ ...user, role: 'admin', isLoggedIn: true });
  const handleLogout = () => setUser({ ...user, role: 'guest', isLoggedIn: false });

  return (
    <div>
      <h1>{user.greeting}</h1>

      <nav>
        <ul>
          {user.menuItems.map(item => (
            <li key={item}>{item}</li>
          ))}
        </ul>
      </nav>

      <div>
        <button onClick={handleLogin}>Login as Admin</button>
        <button onClick={handleLogout}>Logout</button>
      </div>

      <button disabled={!user.canEdit}>
        {user.canEdit ? 'Edit Mode' : 'Read Only'}
      </button>
    </div>
  );
};

当你点击 Login as Admin 时:

  1. user.role 变为 ‘admin’。
  2. Proxy 拦截了这个变化。
  3. menuItems 计算函数重新执行,返回新数组。
  4. canEdit 计算函数重新执行,返回 true
  5. 组件重新渲染,菜单变了,按钮变成了可点击状态。

这一切都发生在毫秒之间,而且代码逻辑完全解耦。你不需要在 handleLogin 里手动去 setState 你的菜单状态,也不需要写 useEffect 来监听 role 的变化。


第九部分:总结与反思

我们今天从 React 状态管理的痛点出发,引入了 JavaScript 的 Proxy 对象,构建了一个基于 Proxy 的派生状态计算引擎。

优点:

  1. 声明式与命令式的完美结合:你用命令式的方式修改数据(proxy.foo = 5),但得到了声明式的响应结果。
  2. 消除样板代码:不再需要为每个派生值写 useStateuseEffect
  3. 自动缓存与失效:我们可以轻松实现基于依赖的缓存机制。
  4. 灵活性:你可以定义任意复杂的计算逻辑,只要它是纯函数。

缺点与风险:

  1. 学习曲线:Proxy 是一个相对高级的 ES6 特性,对于初级开发者来说可能难以理解。
  2. 调试困难:当计算出错时,因为是在 get 中发生的,很难像普通函数调用那样在控制台轻松追踪堆栈。
  3. 性能开销:虽然 Proxy 本身很快,但频繁的 get/set 拦截和对象引用传递可能会带来微小的性能损耗,尤其是在极端高频的渲染场景下。
  4. 不可变性的迷失:它会诱惑你写出可变代码,这在 React 中是一把双刃剑。

最后的建议:

不要为了用 Proxy 而用 Proxy。如果你的项目很简单,或者你只是需要几个简单的计算值,标准的 useMemouseEffect 足够好用,也更容易被维护。

但是,如果你正在处理一个复杂的状态流,或者你厌倦了在 useEffect 里写那些重复的同步逻辑,那么 Proxy 就是你的救星。它是 JavaScript 语言特性的直接应用,是 React 社区(如 MobX)一直在探索的答案。

掌握 Proxy,你就掌握了 React 状态管理的“内功”。现在,拿起你的代码,去改造那些旧项目吧!记得,代码写得好,下班走得早。祝你们编码愉快!

(完)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注