4

I have an issue with updating the immutable redux and quite nested data. Here's an example of my data structure and what I want to change. If anyone could show me the pattern of accessing this update using ES6 and spread operator I would be thankful.

const formCanvasInit = {
  id: guid(),
  fieldRow: [{
    id: guid(),
    fieldGroup: [
      { type: 'text', inputFocused: true }, // I want to change inputFocused value
      { type: 'text', inputFocused: false },
    ],
  }],

  // ...
};
Hastalab
  • 78
  • 1
  • 6
  • I don't know anything about redux, but as you says it's Immutable, which mean you cannot modify or updating it... :D – Hayi Nukman Feb 01 '18 at 04:25
  • Immutability in this context means we should not modify data directly, therefore we have to make a shallow copy first then we can modify the copied data. – Hastalab Feb 01 '18 at 04:28
  • You don't want a shallow copy; you want a true copy, or edits like this to nested attributes will permeate to the original state, which can lead to issues in flux architecture. – treyhakanson Feb 01 '18 at 04:39
  • @treyhakanson Yes, I mean Copying All Levels of Nested Data. – Hastalab Feb 01 '18 at 04:52
  • Possible Duplicate of [How to update a nested state in React](https://stackoverflow.com/questions/43040721/how-to-update-a-nested-state-in-react/43041334#43041334) – Shubham Khatri Feb 01 '18 at 05:42

3 Answers3

3

This should do the trick, assuming the data is set up exactly as shown, with the given array indices:

const newData = {
   ...formCanvasInit,
   fieldRow: [{
      ...formCanvasInit.fieldRow[0],
      fieldGroup: [
         { ...formCanvasInit.fieldRow[0].fieldGroup[0], inputFocused: newValue },
         ...formCanvasInit.fieldRow[0].fieldGroup.slice(1, formCanvasInit.fieldRow[0].fieldGroup.length)
      ]
   }]
};

If index of the element to be changed is to be determined dynamically, you'll need to use functionality such as filter to find and remove the array element you're updating, and then spread the corresponding subarrays by editing the structure of the call to slice.

treyhakanson
  • 4,611
  • 2
  • 16
  • 33
  • This solution seems to be working, thanks mate. I really appreciate your help. Would you mind to give me a reference to read? I really need to learn this particular thing. – Hastalab Feb 01 '18 at 04:47
  • Not a problem; it's a bit length, but the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) on the spread operator covers basically everything you'd need to know. Here's a quicker read from the [Redux documentation](https://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html) as well – treyhakanson Feb 01 '18 at 04:50
  • I don't know if this works if we are trying to update something thats not the first index of fieldRow – ozn Jul 13 '20 at 20:57
2

Try using Immutability Helper

I think in your structure, like this

let news = update(formCanvasInit, {
  fieldRow: [{
    fieldGroup: [
     { $set: {type: "number", inputFocused: false}}
    ]
  }]
})

I've tried it Click Me

Azam
  • 23
  • 4
  • Thanks, this one also works, I might consider refactoring my codebase using this helper. I have a question, how to get the inputFocused value, because i want to make a toggle for inputFocused, when a certain action is happening. I want to do something like //... inputFocused: !inputFocused How can I achieve that? – Hastalab Feb 01 '18 at 07:14
1

This is a longer solution but might help you as your redux state grows. I've also changed some of the values in the original state to make a clearer explanation.

const formCanvasInit = {
  id: 'AAAAXXXX',
  fieldRow: [
    {
      id: 1001,
      fieldGroup: [
        {type: 'text1', inputFocused: true}, // I want to change inputFocused value
        {type: 'text2', inputFocused: false},
      ]
    },
    {
      id: 1002,
      fieldGroup: [
        {type: 'text3', inputFocused: true},
        {type: 'text4', inputFocused: true},
      ]
    }
  ]
};

// the id of the field row to update
const fieldRowID = 1001;
// the value of the field type to update
const fieldTypeValue = 'text1';
const fieldRow = [...formCanvasInit.fieldRow];

// obtain the correct fieldRow object
const targetFieldRowIndex = formCanvasInit.fieldRow.findIndex(fR => fR.id === fieldRowID);
let fieldRowObj = targetFieldRowIndex && formCanvasInit.fieldRow[targetFieldRowIndex];

// obtain that fieldRow object's fieldGroup
const fieldGroup = [...fieldRowObj.fieldGroup];

// obtain the correct object in fieldGroup
const fieldIndex = fieldGroup.findIndex(fG => fG.type === fieldTypeValue);
const fieldToChange = fieldIndex && fieldGroup[fieldIndex];

// replace the old object in selected fieldGroup with the updated one
fieldGroup.splice(fieldIndex, 1, {...fieldToChange, inputFocused: false});

// update the target fieldRow object
fieldRowObj = {...fieldRowObj, fieldGroup};

// replace the old fieldGroup in selected fieldRow with the updated one
fieldRow.splice(targetFieldRowIndex, 1, fieldRowObj);

// create the new formCanvasInit state
const newFormCanvasInit = {...formCanvasInit, fieldRow};
  • I'm a little bit overwhelmed seeing this long code, but it helps me to think in a different way. I appreciate that – Hastalab Feb 01 '18 at 08:03
  • I'm a bit curious of why you need to store a input's focus state in the reducer. Can you manage this state using the input component's internal state instead? – Arie M. Prasetyo Feb 01 '18 at 08:14
  • Honestly, that's not the real data state that I want to use on my app, I just trying to nail the concept of immutability update. This question was one of my way to understand how people solve this kind of case, I want to start with the simplest one. – Hastalab Feb 01 '18 at 09:29