React - Use JavaScript Proxy Object to update with Custom Hook

19 views Asked by At

I am seeing a weird behavior with a simple custom React Hook I am trying to come up with. Any ideas on why it's not working as intended would be greatly appreciated.

So I have a custom hook called 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);
}

Basically, I am returning a proxy that returns the state on the getter and sets the state on a setter.

So the odd behavior comes in when I am trying to use this custom hook. Below is my simple App component.

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>
    );
}

I have it set up so that when I click the button, my proxy state's value should increase by 1. The issue is, when first loading the app and clicking the button, nothing happens (state remains at 0).

But as soon as I change the value in the input box, the state in the button updates and then when clicking, the state reacts as intended.

Does anyone know what may be causing this? Meaning why do I need to update the input's state before the button's state will become reactive?

I have tried multiple "hacks" to try and get this to work, but I think I am missing something internal to React at this point.


Edit: Here is the working code for anyone that is interested. Thanks @CertainPerformance

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); //If initial State is a primitive, use <proxy>.value to access

    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>
    );
}
0

There are 0 answers