Open SKaistrenko opened 3 days ago
I don't think it's related to Safari. Proxy
seems not be possible to be clone with structuredClone
I've tried running this in Chrome which result in the same error:
x = new Map([[1,1]]); structuredClone(new Proxy(x, {})) === x;
// throws: Uncaught DataCloneError: Failed to execute 'structuredClone' on 'Window': #<Map> could not be cloned.
This looks to me as working as intended, drafts are very special objects and should never leave the produce function by any other means than returning it. Why are you structuralCloning those? The output of immer is immutable anyway, so that seems completely unnecessary?
On Fri, Oct 25, 2024, 12:17 PM Ori Livni @.***> wrote:
It's not related to Safari. Proxy seems not be possible to be clone with structuredClone
I've tried running this in Chrome which result in the same error:
x = new Map([[1,1]]); structuredClone(new Proxy(x, {})) === x;// throws: Uncaught DataCloneError: Failed to execute 'structuredClone' on 'Window': #
— Reply to this email directly, view it on GitHub https://github.com/immerjs/immer/issues/1149#issuecomment-2438626999, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN4NBDQNJRI2DKJKPLDUYLZ5KKLLAVCNFSM6AAAAABQNRVGIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZYGYZDMOJZHE . You are receiving this because you are subscribed to this thread.Message ID: @.***>
Note that there is also current if really needed if you want to leak a snapshot, although using it is usually a code smell imho https://immerjs.github.io/immer/current
On Fri, Oct 25, 2024, 12:39 PM Michel Weststrate @.***> wrote:
This looks to me as working as intended, drafts are very special objects and should never leave the produce function by any other means than returning it. Why are you structuralCloning those? The output of immer is immutable anyway, so that seems completely unnecessary?
On Fri, Oct 25, 2024, 12:17 PM Ori Livni @.***> wrote:
It's not related to Safari. Proxy seems not be possible to be clone with structuredClone
I've tried running this in Chrome which result in the same error:
x = new Map([[1,1]]); structuredClone(new Proxy(x, {})) === x;// throws: Uncaught DataCloneError: Failed to execute 'structuredClone' on 'Window': #
— Reply to this email directly, view it on GitHub https://github.com/immerjs/immer/issues/1149#issuecomment-2438626999, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN4NBDQNJRI2DKJKPLDUYLZ5KKLLAVCNFSM6AAAAABQNRVGIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZYGYZDMOJZHE . You are receiving this because you are subscribed to this thread.Message ID: @.***>
TLDR: In my understanding immer modifications to original data are leaking out beyond RTK/produce.
We are not trying to run structuredClone
within produce - this just seems the most straightforward repro.
We use Redux RTK and some of the data we're storing includes Set
.
After that we're selecting this data within the app cycle and do a buck-up using indexed DB.
It is my understanding that immer is by reference modifying the underlying objects we're storing, resulting in the described behaviour.
I'm able to confirm the above assumption by exporting the Set in question and checking the ===
before putting into IndexedDB.
Meaning
const someSet = new Set();
const initialState = { blah: someSet };
...
const stateData = <selectState>;
someSet === stateData.blah // true
If I log the Object.keys of the above set before the RTK slice logic, I get an empty list
In the data I select the above return `[add, delete,
@SKaistrenko my first observation is that you shouldn't be putting Sets into Redux state anyway:
It is my understanding that immer is by reference modifying the underlying objects we're storing
That understanding is wrong. Not doing that is kinda the point of the whole library (as covered on https://immerjs.github.io/immer/ frontpage :)) draft objects are mutable, yes, but neither the inputs or outputs of produce are. So draft objects (they are temporarily mutable copies) behave entirely different from what you get back from produce.
So in either case structuredClone seems to be a strange thing to use icm with immer. If you structureClone the output of produce, that is moot since you could just directly have handed out the reference itself since it is immutable. If you do it inside produce, you are leaking objects that are temporal and this is by design a thing that you should't be doing (or needing. Why would you even share a ref to an object in the middle of some update process?). Just as you wouldn't be sharing the input argument of a react useState updater function with anything that outlives that very function.
@SKaistrenko my first observation is that you shouldn't be putting Sets into Redux state anyway:
Agreed
If you do it inside produce, you are leaking objects that are temporal and this is by design a thing that you should't be doing
The only reason I exported that reference is to validate my assumption that immer messed with the original ref - which doesn't sound ideal.
🐛 Bug Report
A clear and concise description of what the bug is.
If your bug report involves classes, check the docs on how classes are handled first. For patches, don't file issues if they are not optimal, only if they are incorrect.
Link to repro
A bug report without a reproduction is not a bug report. Failing to follow this templately is likely to result in an immediate close & lock of the issue.
https://codesandbox.io/p/sandbox/immer-sandbox-forked-nf577m?file=%2Fsrc%2Findex.ts%3A6%2C8&workspaceId=76a181a1-ec65-4f80-815c-e379a177be13
Unfortunately CodeSandbox doesn't support Safari well - you'll need to run manually on Safari. On the link above you can see code work ok in Chrome
To Reproduce
Steps to reproduce the behaviour: In Safari browser Call
structuredClone
with either a Set or a Map passed through immerThe above op is called within
indexedDB
put operation (MDN docs)Observed behaviour
DataCloneError: The object can not be cloned., value: [object Object]
Expected behaviour
No error - as with plain Map and Set that work well.
Environment
Latest immer, Safari
setUseProxies(true)
setUseProxies(false)
(ES5 only)