Issue/Troubleshooting: Updating a List inside a Map using ImmutableJS

Issue

I have a page that shows many expense cards.  In those expense cards, I can have many expenses for each one.  The initial data may look like the following after 3 cards get added:

  1. [
  2. {
  3. id: 1,
  4. expenses: []
  5. },
  6.  
  7. {
  8. id: 2,
  9. expenses: []
  10. },
  11.  
  12. {
  13. id: 3,
  14. expenses: []
  15. },
  16. ]

Now, when you click the "Add Expense" button, you want to make sure that expense is added to that respective expense card.  As I click on the button, expenses don't show up.  The page doesn't "refresh".  As I start outputting information to the console, I noticed that the state was always empty being returned to the components.  The following is how I want the data to be in the end after expenses are added to the cards:

  1. [
  2. {
  3. id: 1,
  4. expenses: [ { id: 1, cardId: 1 }, { id: 2, cardId: 1 } ]
  5. },
  6.  
  7. {
  8. id: 2,
  9. expenses: [ { id: 3, cardId: 2 }, { id: 4, cardId: 2 } ]
  10. },
  11.  
  12. {
  13. id: 3,
  14. expenses: [ { id: 5, cardId: 2 }, { id: 6, cardId: 2 } ]
  15. },
  16. ]

After a few hours of tinkering and looking around on Stack Overflow, I found a solution that works well using ImmutableJS.

Solution

The following is the code that I used to update the List that was inside the Map (Object):

  1. state = state.update(
  2. state.findIndex(function(item) {
  3. return item.get('id') === action.payload.cardId;
  4. }), function(item) {
  5. let newExpenses = item.get('expenses');
  6.  
  7. // Do something with the list of expenses
  8.  
  9. return item.set('expenses', newExpenses);
  10. }
  11. )
  12.  
  13. return state;

Explanation

First, we call the update() method on the state.  The state will be an Immutable.List and the update() method is something that can be called on that object.  

The first parameter, we pass a function which is actually the findIndex() method.  The findIndex() method takes a predicate as an argument.  A predicate is a logical expression that evaluates to TRUE or FALSE, a boolean.  

For the second parameter we're passing in a function that has an argument called "item".  This argument is the value from the returned index of the array from the first parameter.  In this function, we can do whatever we want with that value, "item".  That value is actually our JS object for an expense card, but it is of type Immutable.Map, which is why when we want to get the List of expenses we use get('expenses').  We then return the item at the end of the function.