unadlib / mutative

Efficient immutable updates, 2-6x faster than naive handcrafted reducer, and more than 10x faster than Immer.
http://mutative.js.org/
MIT License
1.53k stars 16 forks source link

TypeError: Cannot perform 'get' on a proxy that has been revoked #10

Closed relsunkaev closed 1 year ago

relsunkaev commented 1 year ago

I have a use case where I access the base state in an onSuccess callback of a mutation. Something along the lines of

create((base) => {
  // Make changes to base
  mutation.mutate(..., { onSuccess: () => ctx.route.invalidate(base.id) })
})

However, it seems like base is a proxy object that gets revoked at some point between the call to mutation.mutate() and the call to onSuccess(). Currently I'm copying the base into a separate object before every mutation like this

create((base) => {
  // Make changes to base
  base = { ...base }
  mutation.mutate(..., { onSuccess: () => ctx.route.invalidate(base.id) })
})

but that is not ideal.

Is mutative actually passing a proxy object as base? If so, is this addressable on your end and/or are there any other workarounds I could use?

Note: this issue does not occur if running React in Strict Mode.

unadlib commented 1 year ago

@relsunkaev Mutative just drafts the baseState. I'm not sure how you're using it, but one thing is certain: the draft tree should not be used directly in any external function. You may encounter draft escaping, for example:

  let a: any;
  const state = create({ a: { b: 1 } }, (draft) => {
    draft.a.b = 2;
    a = draft.a;
  });
  // Don't use 'a', 'a' is a draft.
  expect(() => {
    JSON.stringify(a);
  }).toThrowError(`Cannot perform 'get' on a proxy that has been revoked`);
  // state.a is a object, not a draft

It would be helpful if you could provide a minimal example to help identify and troubleshoot the issue.

relsunkaev commented 1 year ago

Yeah that was exactly what I was doing wrong. Closing the issue.