hookstate icon indicating copy to clipboard operation
hookstate copied to clipboard

Add on State.get() check and error to the console (WAS: StateMethods.merge does not detected recursive Proxy wrapping.)

Open 98mux opened this issue 5 years ago • 8 comments
trafficstars

I am using React-Select (https://react-select.com/home), where the way of updating the state is to give all values at once (instead of giving just the newly added element and use state.merge())

I see that when you call .get() the state is wrapped around some kind of Proxy element? (It says Proxy in Console when I print the object) So that whenever I get a new element, all the old elements get wrapped around a new Proxy element. This nesting continues until I get a "Uncaught RangeError: Maximum call stack size exceeded" error.

Is there a way to unwrap these Proxy elements before I set the state?

Did this make sense? :P

Also, I'm loving hookstate! Keep up the good work :D Definitely going to use this for all my future projects!

98mux avatar Jul 15 '20 12:07 98mux

doing () => {state.get()} solved this somehow :shrug:

also state.get().anything worked ???

98mux avatar Jul 15 '20 13:07 98mux

Sorry, I do not understand what your problem is. It looks like you ended up with nested states set to values of state object. 'set' method traps it and should raise this exception: https://hookstate.js.org/docs/exceptions#hookstate-102 Maybe merge method misses this checking, I will have a look.

Is there a way to unwrap these Proxy elements before I set the state?

If you give me more complete example of what you are doing, I might answer your question.

where the way of updating the state is to give all values at once (instead of giving just the newly added element and use state.merge()

To set the value at once, use this: https://hookstate.js.org/docs/typedoc-hookstate-core#set You can learn more details here: https://hookstate.js.org/docs/nested-state#advanced-mutations-for-an-object-state

avkonst avatar Jul 15 '20 22:07 avkonst

please, note: state.get() also returns a proxy for object values. So I need to see more details of what you are doing, to help you. Maybe you can publish reproducer project?

avkonst avatar Jul 15 '20 22:07 avkonst

Also, I'm loving hookstate! Keep up the good work :D Definitely going to use this for all my future projects!

Thanks. Could you please help the project to grow by spreading few words about in blogs and communities? Thanks

avkonst avatar Jul 15 '20 22:07 avkonst

Reopen to keep it as a reminder to have a look how to prevent recursive proxy wrapping in merge method.

avkonst avatar Jul 15 '20 22:07 avkonst

I did a bad job at explaining :P

export default function  SelectPage() {
const selectedOptions = useState([]);

const handleChange = option => {
//Option are all the options selected so far, and not just the newly selected option
selectedOptions.set(option);
}


return (
<Select
value={selectedOptions.get()}
onChange={handleChange}
/>
);
}

Selecting the first option works fine. We get [Proxy{option1}], however when selecting the next option i get the error "Uncaught RangeError: Maximum call stack size exceeded". This is because selectedOptions is now [Proxy{Proxy{option1}},Proxy{option2}]

This fixes the problem:

value= {selectedOptions.get().anything}

as i found from here https://stackoverflow.com/questions/51096547/how-to-get-the-target-of-a-javascript-proxy

Maybe it would be nice with a function like selectedOptions.getUnwrapped()?

Im on Hookstate 3.0.0 btw

98mux avatar Jul 16 '20 07:07 98mux

Ok. Do this: const selectedOptions = useState([]).attach(Downgraded); It will help to disable proxy returning from selectedOptions.get(). The complete example would be:

export default function  SelectPage() {
const selectedOptions = useState([]).attach(Downgraded);

const handleChange = option => {
//Option are all the options selected so far, and not just the newly selected option
selectedOptions.set(option);
}


return (
<Select
value={selectedOptions.get()}
onChange={handleChange}
/>
);
}

avkonst avatar Jul 16 '20 22:07 avkonst

Anybody still facing this you can use selectedOptions.get({noproxy:true}), it will give plain objects rather than wrapped in proxy.

ankitsinghh121 avatar Mar 03 '23 09:03 ankitsinghh121