chobotek
chobotek

Reputation: 77

React redux - updating nested array in state

I'm trying some app in react redux and i have a problem with updating (push, remove, update) the nested array in state.

I have some object called service like this:

{
    name: 'xzy',
    properties: [
       { id: 1, sName: 'xxx'},
       { id: 2, sName: 'zzz'}, 
    ]
}

Whatever I did (in case of adding property to collection) in the reducer with the properties collection generate problem that all properties got same values as the last I had recently added -> Added property object is in service properties collection but the action replace all values in all properties in this collection. My reducer:

export function service(state = {}, action) {
        switch (action.type) {
            case 'ADD_NEW_PROPERTY':
                console.log(action.property) // correct new property
                const service = {
                    ...state, properties: [
                        ...state.properties, action.property
                    ]
                }
                console.log(service); // new property is pushed in collection but all properties get same values
                return service

            default:
                return state;
        }
    }

I have tried some solution with immutability-helper library and it generate the same problem:

export function service(state = {}, action) {
    case 'ADD_NEW_PROPERTY':

                return update(state, {properties: {$push: [action.property]}})

            default:
                return state;
        }

For example when I add new property { id: 1, sName: 'NEW'} to example above I will get this state:

{
    name: 'xzy',
    properties: [
       { id: 1, sName: 'NEW'},
       { id: 1, sName: 'NEW'}, 
       { id: 1, sName: 'NEW'}
    ]
}

Can someone help? :)

Upvotes: 1

Views: 2762

Answers (4)

soupette
soupette

Reputation: 1280

I'd recommend you to use Immutable data https://facebook.github.io/immutable-js/docs/#/List

import { fromJS, List } from 'immutable';

const initialState = fromJS({
  propeties: List([{ id: 1, sName: 'xyz' }]
}

function reducer(state = initialState, action) {
  case ADD_NEW_PROPERTY:
    return state
      .update('properties', list => list.push(action.property));
 // ...
}

Upvotes: 1

André Werlang
André Werlang

Reputation: 5964

Make a copy of action.property as well. Whatever is dispatching this action, it could be reusing the same object.

export function service(state = {}, action) {
  switch (action.type) {
    case 'ADD_NEW_PROPERTY':
      console.log(action.property) // correct new property
      const service = {
        ...state,
        properties: [
          ...state.properties, 
          { ...action.property }
        ]
      }
      console.log(service); // new property is pushed in collection but all properties get same values
      return service

    default:
      return state;
  }
}

Upvotes: 2

mxmln
mxmln

Reputation: 21

You should always copy the state before returning it.

  export default function(state = {}, action) {
  switch(action.type) {
    case 'GET_DATA_RECEIVE_COMPLETE': {
      const data  = action.firebaseData;
      const newState = Object.assign({}, state, {
        data
      });

      return newState
    }
    default:
      return state;

  }
}

Upvotes: 0

Berry M.
Berry M.

Reputation: 489

Your service reducer should probably look somewhat like this:

// Copy the state, because we're not allowed to overwrite the original argument
const service = { ...state }; 
service.properties.append(action.property)
return service

Upvotes: 0

Related Questions