16

This is how example of my state looks:

const INITIAL_STATE = {
 contents: [ {}, {}, {}, etc.. ],
 meta: {}
}

I need to be able and somehow replace an item inside contents array knowing its index, I have tried:

      return {
        ...state,
        contents: [
          ...state.contents[action.meta.index],
          {
            content_type: 7,
            content_body: {
              album_artwork_url: action.payload.data.album.images[1].url,
              preview_url: action.payload.data.preview_url,
              title: action.payload.data.name,
              subtitle: action.payload.data.artists[0].name,
              spotify_link: action.payload.data.external_urls.spotify
            }
          }
        ]
      }

where action.meta.index is index of array item I want to replace with another contents object, but I believe this just replaces whole array to this one object I'm passing. I also thought of using .splice() but that would just mutate the array?

1
  • 1
    Also, take a look at React's Immutability helpers - facebook.github.io/react/docs/update.html They are not as hard to use as they look and really makes your code easier to read. Commented Feb 12, 2016 at 15:04

3 Answers 3

24

Note that Array.prototype.map() (docs) does not mutate the original array so it provides another option:

 const INITIAL_STATE = {
   contents: [ {}, {}, {}, etc.. ],
   meta: {}
 }

 // Assuming this action object design
 {
   type: MY_ACTION,
   data: {
     // new content to replace
   },
   meta: {
     index: /* the array index in state */,
   }
 }

 function myReducer(state = INITIAL_STATE, action) {
   switch (action.type) {
     case MY_ACTION: 
       return {
         ...state,
         // optional 2nd arg in callback is the array index
         contents: state.contents.map((content, index) => {
           if (index === action.meta.index) {
             return action.data
           }

           return content
         })
       }
   }
 }
Sign up to request clarification or add additional context in comments.

1 Comment

Definitely the most elegant solution.
13

Just to build on @sapy's answer which is the correct one. I wanted to show you another example of how to change a property of an object inside an array in Redux without mutating the state.

I had an array of orders in my state. Each order is an object containing many properties and values. I however, only wanted to change the note property. So some thing like this

let orders = [order1_Obj, order2_obj, order3_obj, order4_obj];

where for example order3_obj = {note: '', total: 50.50, items: 4, deliverDate: '07/26/2016'};

So in my Reducer, I had the following code:

return Object.assign({}, state,
{
  orders: 
    state.orders.slice(0, action.index)
    .concat([{
      ...state.orders[action.index],
      notes: action.notes 
    }])
    .concat(state.orders.slice(action.index + 1))
   })

So essentially, you're doing the following:

1) Slice out the array before order3_obj so [order1_Obj, order2_obj]

2) Concat (i.e add in) the edited order3_obj by using the three dot ... spread operator and the particular property you want to change (i.e note)

3) Concat in the rest of the orders array using .concat and .slice at the end .concat(state.orders.slice(action.index + 1)) which is everything after order3_obj (in this case order4_obj is the only one left).

1 Comment

this helped me so much. ty for this
6

Splice mutate the array you need to use Slice . And you also need to concat the sliced piece .

return Object.assign({}, state,  {
         contents:
          state.contents.slice(0,action.meta.index)
          .concat([{
            content_type: 7,
            content_body: {
              album_artwork_url: action.payload.data.album.images[1].url,
              preview_url: action.payload.data.preview_url,
              title: action.payload.data.name,
              subtitle: action.payload.data.artists[0].name,
              spotify_link: action.payload.data.external_urls.spotify
            }
          }])
          .concat(state.contents.slice(action.meta.index + 1))
  }

2 Comments

Edited .I guess you are trying to send the entire state , with content value changed. So, use Object.assign to send new state , and if there are multiple sources for same key in Object.assign , last source wins .
Thnx. saved my life.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.