0

I need a little help with updating an inner element of an object using Object.assign in my redux reducer.

To better explain the scenario, I'm using NBA teams. I have an NBA object in my redux store. I'm now trying to update only the teamMembers for a particular team. My NBA object looks like this:

{
   teams: [
       {
          teamId: 123,
          teamName: "Los Angeles Lakers"
          teamMembers: [
             {id: "kbryant", fullName: "Kobe Bryant"},
             {id: "bingram", fullName: "Brandon Ingram"}
          ]
        },
        {
          teamId: 234,
          teamName: "Miami Heat"
          teamMembers: [
             {id: "cbosh", fullName: "Chris Bosh"},
             {id: "tjohnson", fullName: "Tyler Johnson"}
          ]
        }
   ]
}

I pass both teamId and members to the reducer through my action i.e. action.teamId and action.members.

This is what I have so far:

Object.assign({}, state, {
   nba: Object.assign({}, state.nba, {
      teams: Object.assign({}, state.nba.teams, {
         teamId[action.teamId] // I'm stuck here...
      })
   })
})

How do I update the teamMembers of a particular team? I know I'm almost there but could use a little help. Thanks.

Sam
  • 26,817
  • 58
  • 206
  • 383
  • 1
    Possible duplicate of [Updating nested data in redux store](http://stackoverflow.com/questions/32135779/updating-nested-data-in-redux-store) – Nikolaj Dam Larsen Dec 27 '16 at 23:07
  • Not a duplicate. Also the suggestions for that question don't apply. I'm using Object.assign only. – Sam Dec 27 '16 at 23:10
  • You could wrap the relevant `Object.assign` call in an [IIFE](http://benalman.com/news/2010/11/immediately-invoked-function-expression/) and do some work inside of there, including any data mutation. Just make sure to mutate and `return` the new object made by `Object.assign()`. – Seth Holladay Dec 27 '16 at 23:13
  • Are you trying to add or remove an object from `teamMembers`? – guest271314 Dec 27 '16 at 23:14
  • @Sam You're trying to achieve the same thing and using `update` from the immutability-helper is a valid solution. – Nikolaj Dam Larsen Dec 27 '16 at 23:14
  • Neither! Simply replace the value of teamMembers. The action.members contains all the members. – Sam Dec 27 '16 at 23:14
  • Why is `Object.assign()` necessary? – guest271314 Dec 27 '16 at 23:19
  • How else can I update the state keeping everything exactly the same except for a particular team's members? – Sam Dec 27 '16 at 23:20
  • @NikolajDamLarsen I avoid using any third party solutions as much as possible. Looks like the immutability helper is already deprecated. Now they're suggesting yet another third party helper. This is why I avoid using third party solutions. I really should be able to handle this scenario using simply Object.assign – Sam Dec 27 '16 at 23:21
  • Have not tried `reactjs` or `redux`. Assigning directly to the object does not have same effect? – guest271314 Dec 27 '16 at 23:22
  • @guest271314 No, because we cannot mutate things in redux. That's why we use Object.assign so that we create a new copy of an existing object and put that in redux store. – Sam Dec 27 '16 at 23:23
  • I suggest storing teams as a map - replace array by object with IDs as keys. this way, it's much easier to update particular entity (this applies to any entity collection). Then, do follow link in very first comment and use `update` helper shipped by React team. – Andreyco Dec 27 '16 at 23:26

1 Answers1

1

You could extend the code you had with a dynamic index property, which is intended to identify which entry in the teams array needs updating. The ES6 syntax allows dynamic properties in object literals, by wrapping the dynamic expression in square brackets, like this: { [index]: value }, where index is a variable or other expression.

Here is the final solution:

function getNewState(state, action) {
    var index = state.nba.teams.findIndex( t => t.teamId == action.teamId );
    if (index == -1) return state; // team not found
    return Object.assign({}, state, {
        nba: Object.assign({}, state.nba, {
            teams: Object.assign([], state.nba.teams, {
                [index]: Object.assign({}, state.nba.teams[index], { 
                    teamMembers: action.teamMembers 
                })
            })
        })
    });
}

var state = {
    nba: {
        teams: [{
            teamId: 123,
            teamName: "Los Angeles Lakers",
            teamMembers: [
                {id: "kbryant", fullName: "Kobe Bryant"},
                {id: "bingram", fullName: "Brandon Ingram"}
            ]
        }, {
            teamId: 234,
            teamName: "Miami Heat",
            teamMembers: [
                {id: "cbosh", fullName: "Chris Bosh"},
                {id: "tjohnson", fullName: "Tyler Johnson"}
            ]
        }]
    }
};

var action = { teamId: 123, teamMembers: [{id: "lnance", fullName: "Larry Nance"}] };
var newState = getNewState(state, action);
console.log(newState);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Note that this kind of manipulation becomes a lot easier with a library like Immutable.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • I think this is a pretty good solution i.e. figuring out the index then updating the values. It still took me a while to get it to work. After debugging it for a while trying to figure out what's going on, I realized during the update -- because I'm using Object.assign, I was converting teamMembers from an array to an object which ended up breaking the rest of my code. So instead of using Object.assign to create a new copy of teamMembers, I used slice instead and it worked fine. I will look into the immutable library. Thanks. – Sam Dec 28 '16 at 00:44
  • Good to hear it worked out. I think `Object.assign` should also work, but by providing an empty array as first argument, to make sure it is always an array: `Object.assign([], ...)`. I updated it just now. But `slice` is fine also of course. – trincot Dec 28 '16 at 00:55
  • I didn't know I could use Object.assign with an empty array. Just learned one more thing. You've been super helpful. Really appreciate it! I'll up-vote your answer. – Sam Dec 28 '16 at 00:58