Why immer.js doesn't allow setting dynamic properties on draft?
Asked Answered
F

1

5

//I want my action to dispatch payload like
// {type:'update',payload:{'current.contact.mobile':'XXXXXXXXX'}}
//In reducer dynamically select the segment of state update needs to be applied to 
//Below code doesn't work as expected though, draft always remains at same level
draft = dA.key.split('.').reduce((draft, k) => {
  return draft[k]
}, draft);

//Or an ideal syntax may look like below line
draft['current.contact.mobile'] = dA.value;


//Code that works
draft['current']['contact']['mobile'] = dA.value;
I want my action to dispatch payload like {type:'update',payload:{'current.contact.mobile':'XXXXXXXXX'}} And in reducer dynamically select the segment of state that needs to be updated. Is there something fundamentally wrong in doing this, I believe this could make life easier. Is there something that can done to achieve this ?
Faviolafavonian answered 27/12, 2019 at 15:7 Comment(0)
R
7

In your case, this code returns a primitive value like a string or number which is immutable.

draft = dA.key.split('.').reduce((draft, k) => {
  return draft[k]
}, draft);

"Immer" is using Proxy to implement all this magic. The proxy could work only on objects for example Object, Array, Function etc.

so to fix your problem you can use code like this

import produce from "immer";

describe("Why immer.js doesn't allow setting dynamic properties on draft?", function() {
  it("should allow set dynamic properties", function() {
    const path = "foo.bar.zoo";
    const state = { foo: { bar: { zoo: 1 } } };
    const nextState = produce(state, draft => {
      const vector = path.split(".");
      const propName = vector.pop();

      if (propName) {
        draft = vector.reduce((it, prop) => it[prop], draft);
        draft[propName] += 1;
      }
    });

    expect(nextState.foo.bar.zoo).toEqual(state.foo.bar.zoo + 1);
  });
});

In the code above, we get destination object and update the property of this object.

Some note about string and number. Javascript has constructors for string and number which return objects not primitive values. But this is a very rare case when someone uses it explicitly. Usually, we deal with it implicitly when writing something like this dA.key.split('.'). In this case, the interpreter would create a string object and call method "split" on it. Usually, this behavior is referred to as "Boxing"

Rumen answered 30/12, 2019 at 18:12 Comment(1)
Nice explanation, but if anyone wants to hide that logic by using Lodash you can do set(draft, path, newValue)Ewall

© 2022 - 2024 — McMap. All rights reserved.