Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
3 tasks done
jpallen opened this issue May 10, 2022 · 6 comments

Comments

@jpallen
Copy link
Contributor

jpallen commented May 10, 2022

馃悰 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 #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 & 285fff9
  • I filed this report against the latest version of Immer
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)
@jpallen
Copy link
Contributor Author

jpallen commented May 10, 2022

This seems related to #916, but the fix in #917 doesn't fix the case I've shown above.

@mweststrate
Copy link
Collaborator

mweststrate commented May 10, 2022

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.

@jpallen
Copy link
Contributor Author

jpallen commented May 10, 2022

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"
	}
}

@fantasticsoul

This comment was marked as spam.

@BrianHung
Copy link
Contributor

BrianHung commented Aug 16, 2022

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.

@mweststrate
Copy link
Collaborator

Closing as original issue seems solved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants