immer icon indicating copy to clipboard operation
immer copied to clipboard

Nested and chained produce usage results in error: Cannot perform 'get' on a proxy that has been revoked

Open jpallen opened this issue 3 years ago • 5 comments

🐛 Bug Report

When using nested and chained produce calls, when a property is copied from a child object to the parent, immer throws an error when accessing the property in the final state:

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

Link to repro

PR with failing unit test is in https://github.com/immerjs/immer/pull/935

To Reproduce

The following example code shows this error:

const state = {
	foo: {
		bar: {
			baz: 1
		}
	}
}
const newState = produce(state, draft => {
	draft.foo = produce(draft.foo, fooDraft => {
		fooDraft.baz = fooDraft.bar.baz
	})
	draft.foo = produce(draft.foo, fooDraft => {
		/* another produce call makes this fail */
		/* no actual mutation necessary to make this happen */
	})
})

// Error is thrown here when the property is read
JSON.stringify(newState)
> TypeError: Cannot perform 'get' on a proxy that has been revoked

Observed behavior

An error is throw while reading the modified property

Expected behavior

No error to be throw.

Environment

Only seems to happen with autoFreeze = true.

  • Immer version: Tested on v7.0.0 - v9.0.12 & 285fff927428291559505ec057512811c1951d10
  • [x] I filed this report against the latest version of Immer
  • [x] Occurs with setUseProxies(true)
  • [x] Occurs with setUseProxies(false) (ES5 only)

jpallen avatar May 10 '22 11:05 jpallen

This seems related to https://github.com/immerjs/immer/issues/916, but the fix in https://github.com/immerjs/immer/pull/917 doesn't fix the case I've shown above.

jpallen avatar May 10 '22 11:05 jpallen

Your test creates a non-unidirectional graph, which isn't supported by Immer. There should be only one single path from any node in your tree to the root. After the first assignment, foo.bar.bazs object lives at two different locations in the tree.

mweststrate avatar May 10 '22 17:05 mweststrate

Thanks for the reply! In the real situation where I've encountered this issue, I'm not setting another direct reference to foo.bar.baz, I'm actually setting the result of a string.replace(). I've updated the test to reflect this real usage, and it still fails with the same issue. I had reduced the test case a little too much sorry.

The failing test is then:

const state = {
	foo: {
		bar: {
			baz: "banana"
		}
	}
}
const newState = produce(state, draft => {
	draft.foo = produce(draft.foo, fooDraft => {
		fooDraft.baz = fooDraft.bar.baz.replace("banana", "apple")
	})
	draft.foo = produce(draft.foo, fooDraft => {
		/* another produce call makes this fail */
		/* no actual mutation necessary to make this happen */
	})
})
JSON.stringify(newState)

I would expect newState to equal the following after this code is run, which doesn't reference the same object twice (at least not in vanilla JS, although I appreciate there may be references inside immer to make this all work!):

{
	foo: {
		bar: {
			baz: "banana"
		},
		baz: "apple"
	}
}

jpallen avatar May 10 '22 20:05 jpallen

hey, dude, you can check this online example, it works very well. https://codesandbox.io/s/limu-case1-qyr9yu?file=/src/index.js

image

by the way: limu is a faster immutable lib, hope you like it.

fantasticsoul avatar Jul 11 '22 11:07 fantasticsoul

Same thing happens with createDraft and finishDraft.

let state = createDraft({})
state.x = 10

let patches, inversePatches, nextState
nextState = finishDraft(state, (p, ip) => {
  patches = p
  inversePatches = ip
})

state = createDraft(nextState)
state.x = 20

// Throws
// TypeError: Cannot perform 'get' on a proxy that has been revoked
// as nextState is frozen by `setAutoFreeze`

Edit: The problem is not directly visible in that code, but trying to integrate this into a reactive store, we need keep snapshots or a pointer to the previous state. Is there a workaround to keep a pointer to state? Since isDraft(state) when finishDraft(state) will result in a proxy revoked.

BrianHung avatar Aug 16 '22 08:08 BrianHung

Closing as original issue seems solved

mweststrate avatar Mar 09 '24 21:03 mweststrate