Troubleshooting & FAQ
A collection of the most common questions and pitfalls when working with reactive-proxy-state.
Why doesn't my effect run after I destructure a reactive object?
When you do
const { count } = state; // state is reactive
watchEffect(() => {
console.log(count); // NEVER re-runs
});
you copy the value of count
at that moment – it's a plain primitive, no longer connected to the proxy. Either:
- Use a getter function:ts
watchEffect(() => console.log(state.count));
- Or keep it reactive with
toRefs
/toRef
:tsconst { count } = toRefs(state); watchEffect(() => console.log(count.value));
watch
callback shows the same object for newValue
and oldValue
when I watch an array / Map / Set
watch
compares deeply by default but it still passes the proxy instance to you. Internal mutations (e.g. push
, set
, add
) don't create a new array/collection – they mutate in place – so the reference is identical.
If you need granular diffing:
watch(
() => state.items.slice(), // clone on every access
(newArr, oldArr) => {
// now references differ – do your diff
},
{ deep: false } // shallow compare is enough now
);
Map/Delete mismatch – should I emit delete
or map-delete
?
For operations that originate from a Map
proxy (map.set
, map.delete
, map.clear
) the wrapper already emits the specialised actions: map-set
, map-delete
, map-clear
. You only use plain delete
when you remove a property of a normal object.
How do I cancel expensive async work in watchEffect
?
Pass a cleanup function to the onCleanup
helper:
watchEffect((onCleanup) => {
const ctrl = new AbortController();
fetch("/api", { signal: ctrl.signal });
onCleanup(() => ctrl.abort());
});
The cleanup runs before the effect re-executes and when it is stopped.
Deep watching is slow for huge objects – what can I do?
- Watch a getter that returns only the piece you care about.
- Set
deep: false
and manage nested changes yourself. - Rely on collection-specific actions (
array-splice
,map-set
, etc.) that carry just the diff and apply them withupdateState
.
isReactive
returns false
for my Map / Set
Make sure you access the proxy, not the original collection:
const state = reactive({ myMap: new Map() });
console.log(isReactive(state.myMap)); // true
const raw = state.myMap.__v_raw;
console.log(isReactive(raw)); // false
Can I turn a reactive object back into its raw form?
Yes – use toRaw()
:
const rawUser = toRaw(state.user);
The returned object is the original instance; further proxy mutations won't reflect there.
Triggering an update manually
For a ref
, call triggerRef(refInstance)
. For objects use normal mutation – there's no public API for force-trigger.
Memory leaks when creating many effects in a loop
Always capture the stop handle and dispose it:
const stops = list.map((item) => watchEffect(() => doStuff(item)));
// later
stops.forEach((stop) => stop());
Computed refs also expose .stop()
– call it if you create them dynamically.
Have another issue? File an issue or open a discussion!