2

Im not sure why I'm getting Error: Immer drafts cannot have computed properties in my reducer code. I'm using redux-starter-kit which wraps all my reducer code with the Immer library.

I'm not entirely sure what Immer is referring to with "computed property." Does it mean getters/setters? Because I'm not creating any getters or setters explicitly. Is the object spread operator doing it?

Do getters/setters contaminate the state object somehow?

Or is computed property in reference to computed property names? https://tylermcginnis.com/computed-property-names/

My reducer is fairly simple:

import { createSlice } from 'redux-starter-kit' 

const assets = createSlice({
  slice: 'assets',
  initialState: {byName: {}},
  reducers: {
    upload: (state, action) => {
        const {name} = action.payload;
        state.byName[name].status = 'uploading';
    },
    initialize: (state, action) => {
        const {assets, id} = action.payload;
        assets.forEach(({name, uri}) => {
            state.byName[name] = {
                uri,
                name,
                status: 'local',
                id,
                inProgress: true
            };
        });
    },
  }
})
export default assets;

The assets/initialize action is triggered first with no error, and the immer error occurs when the assets/upload action is triggered

assets/initialize output

I'm not sure where all those get name / set name, get uri / set uri fields are coming from. Is that what Immer is complaining about?

assets/upload output

I rewrote the upload reducer to create a new object,

upload: (state, action) => {
    const {name} = action.payload;
    state.byName[name] = {
        ...state.byName[name],
        status: 'uploading',
    };
},

and the error I get now is equally baffling:

assets/upload error output

frank
  • 1,322
  • 1
  • 10
  • 28
  • You should post a [reprex](https://stackoverflow.com/help/minimal-reproducible-example). Are you using React, Vue or something else? – FINDarkside Aug 20 '19 at 14:52
  • Using React Native. I'll work on a repro. – frank Aug 22 '19 at 03:56
  • Not sure if you ever solved your issue. In my case, I wound up trying to reproduce it, and somehow recreated my entire app without reproducing the problem. In the end, I cleaned yarn's cache, deleted the node_modules folder, and reinstalled my dependencies from scratch -- which solved the problem. Something *very* odd going on here. – RonLugge Nov 09 '19 at 05:45
  • Correction: I actually found the issue. It's my initial state -- electron was messing with the data I was sending back and forth, and that somehow messed my state up completely. – RonLugge Nov 09 '19 at 06:04
  • I've been having this problem as well. I think that fetch is building the results object as a bunch of computed properties, which you are putting directly on state, and then trying to modify with immer. – Matt Way May 29 '20 at 00:20

1 Answers1

-3

I'm not sure where all those get name / set name, get uri / set uri fields are coming from. Is that what Immer is complaining about?

These "fields" come from Redux, and yes, that's what Immer is complaining about. They're called Acessors - but mostly known as Getter and Setter - which were introduced in ES5. This is by design Redux's way to work with state, which is to mutate the object and track changes through these functions - your reducers become setters, and selectors become getters. Now Immer's approach is to provide an immutable state tree, providing a new object on every state change.

Immer complains about these acessors because there's no way to clone object with functions 100% reliably, as these may be accessing lexically-scoped private variables through closures, i.e. these function bodies variable references would point to the previous state (more on that on "how do javascript closures work"). And this isn't an Immer-specific problem, it is the very specification of how the JavaScript structured clone algorithm work. As per MDN comment on the structured clone algorithm:

The structured clone algorithm is an algorithm defined by the HTML5 specification for copying complex JavaScript objects. (...)

Things that don't work with structured cloneSection

  • Error and Function objects cannot be duplicated by the structured clone algorithm; attempting to do so will throw a DATA_CLONE_ERR exception.
  • Attempting to clone DOM nodes will likewise throw a DATA_CLONE_ERR exception.
  • Certain parameters of objects are not preserved:
    • The lastIndex field of RegExp objects is not preserved.
    • Property descriptors, setters, and getters (as well as similar metadata-like features) are not duplicated. For example, if an object is marked read-only using a property descriptor, it will be read-write in the duplicate, since that's the default condition.
    • The prototype chain does not get walked and duplicated.

Note that prototype computed property names do work with Immer if you set it as "immerable" with prototype[immerable] = true, but that's not the case here.

wwmoraes
  • 1
  • 1
  • Im not using Vue – frank Aug 26 '19 at 03:28
  • @frank indeed, but the concept still applies. I've rephrased so it now refers specifically to redux. – wwmoraes Aug 26 '19 at 03:49
  • This cannot be right. I tried this in code sandbox, and it's not true. Here is my sandbox - https://codesandbox.io/s/immer-computed-properties-redux-2mzxf – Noitidart Oct 24 '19 at 23:23
  • I don't think this answer is at all right. Redux doesn't add getters/setters. It may be coming from somewhere else, but redux relies on the coder to create a new object and return that, leaving the original unmutated. ReduxStarterKit adds immer and wraps the reducers in that, so the fields *may* be coming from there, but they're certainly not coming from redux itself. – RonLugge Nov 09 '19 at 00:17