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

自定义React Hook结合JavaScript Proxy更新状态异常问题排查

React自定义Hook useProxyState 异常问题分析与修复

问题场景

实现了一个返回Proxy对象的自定义Hook useProxyState,期望通过getter获取状态、setter更新状态。但在组件中使用时出现异常:首次加载后点击按钮修改count.value,页面状态无变化;修改输入框内容触发组件重渲染后,按钮状态才更新,后续点击可正常响应。

原始代码

自定义Hook useProxyState

import { useState } from 'react';
export function useProxyState(initValue: any) {
    if (typeof initValue !== 'object') initValue = { value: initValue };
    const [state, setState] = useState(initValue);
    const handlers: {} = {
        get: (target: any, prop: string, reciever: any) => {
            return state[prop];
        },
        set: (target: any, prop: string, value: any, reciever: any) => {
            Reflect.set(target, prop, value, reciever);
            setState((prevState: any) => (prevState = target));
            return true;
        },
    };
    return new Proxy(initValue, handlers);
}

组件使用代码

import './App.css';
import { useProxyState } from './Hooks/useProxy/useProxyState';
import { useState } from 'react';

export default function App() {
    const [input, setInput] = useState('');
    const count = useProxyState(0);

    const clickHandler = () => {
        count.value++;
    };

    return (
        <div className='App'>
            <div className='card'>
                <button onClick={clickHandler}>count is {count.value}</button>
            </div>
            <input value={input} onChange={(e) => setInput(e.target.value)} type='text' />
            <div>{input}</div>
        </div>
    );
}

异常原因分析

  1. 状态更新的引用问题:setState回调中prevState = target是赋值操作,最终返回的target与原state是同一个引用。React的状态更新依赖引用变化检测,当传入的新状态与旧状态引用相同时,React会判定状态未发生改变,不会触发组件重渲染。
  2. Proxy目标对象的问题:原始代码中Proxy的目标对象是initValue,该对象在组件首次渲染后会被保留,后续状态更新时始终操作同一个对象,无法触发React的状态变更检测。

修复方案

修复后的useProxyState代码

import { useState } from 'react';

export function useProxyState(obj: any) {
    if (typeof obj !== 'object') obj = { value: obj };
    const [state, setState] = useState(obj);
    const handlers: {} = {
        get: (target: any, prop: string, reciever: any) => {
            return state[prop];
        },
        set: (target: any, prop: string, value: any, reciever: any) => {
            Reflect.set(target, prop, value, reciever);
            setState(target);
            return true;
        },
    };
    let proxy: any = new Proxy({}, handlers);
    return proxy;
}

修复后的组件使用代码

import './App.css';
import { useProxyState } from './Hooks/useProxy/useProxyState';
import { useState } from 'react';

export default function App() {
    const [input, setInput] = useState('');
    let count: any = useProxyState(0); // 初始值为原始类型时,通过 <proxy>.value 访问

    const clickHandler = () => {
        count.value++;
        console.log(count);
    };

    return (
        <div className='App'>
            <div className='card'>
                <button onClick={clickHandler}>count is {count.value}</button>
            </div>
            <input value={input} onChange={(e) => setInput(e.target.value)} type='text' />
            <div>{input}</div>
        </div>
    );
}

修复要点

  • 将Proxy的目标对象改为空对象{},避免与初始状态对象产生引用关联;
  • 修改setState的调用方式,直接传入更新后的target(此时target已通过Reflect.set更新了属性值),确保每次状态更新传递的是新的状态引用;
  • 组件中使用时,明确原始类型初始值的访问方式为proxy.value。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:05:36