简体   繁体   中英

How do I add an element to array in reducer in Redux when the array does not yet exist?

I have a code sandbox to demo an issue I am having with Redux and React.

https://codesandbox.io/s/reactredux-3edy8

Basically this reducer does not work:

const initialState = {
  byId: {}
};

function addReducer(state = initialState, action) {
  switch (action.type) {
    case "RECEIVE_DATA":
      return {
        ...state,
        byId: {
          ...state.byId,
          [action.payload.id]: [
            ...state.byId[action.payload.id],
            action.payload.data
          ]
        }
      };
    default:
      return state;
  }
}

export default addReducer;

here is the action:

import { RECEIVE_DATA } from "./actionTypes";

export function action() {
  return {
    type: RECEIVE_DATA,
    payload: {
      id: Math.floor(Math.random() * 2),
      data: Math.floor(Math.random() * 10000)
    }
  };
}

In my own app it throws an error like state.byId[action.payload.id] is not iterable which is true. But how do I push values to an array that does not yet exist because I do not know the id ?

I thought the issue might be solved by changing the initialState or perhaps there is a function I am not aware of that allows me to either create an array or push to it.

Check if the array exists first. If it does, add a new element. If not, create a new array with one element (the new data from the action)

const initialState = {
  byId: {}
};

function addReducer(state = initialState, action) {
  switch (action.type) {
    case "RECEIVE_DATA":
      return {
        ...state,
        byId: {
          ...state.byId,
          [action.payload.id]: (
              state.byId[action.payload.id] ? [
              ...state.byId[action.payload.id],
              action.payload.data
            ] : [action.payload.data])
        }
      };
    default:
      return state;
  }
}

export default addReducer;

You can read more about ternary statements here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator

I am not sure but I think you need something like this

 function addReducer(state = initialState, action) {
      switch (action.type) {
        case "RECEIVE_DATA":
          return {
            ...state,
            byId: {
              ...state.byId,
              [action.payload.id]: 
                [action.payload.data]

            }
          };
        default:
          return state;
      }
    }

The answer to this problem lies in flattening the state. I am sure there are other ways to fix this problem, but I found this the cleanest.

Typically the byId property of state is created through flattening state per the guidelines in the docs

I take it one step further, making the byId property an Array instead of an Object. I move the id into the objects of the array like in this new sandbox: https://codesandbox.io/s/reactredux-zj1n4

I just have to remember the byId is now an array when I map the state to my components.

No functionality is lost and my byId field can now support multiple different ids if needed (eg if I had many to many relationships on the objects)

Here is the code:

in reducer

case "RECEIVE_DATA":
      return {
        ...state,
        byId: [...state.byId, action.payload.data]
      };

new action:

export function action() {
  return {
    type: RECEIVE_DATA,
    payload: {
      data: {
        id: Math.floor(Math.random() * 2),
        value: Math.floor(Math.random() * 10000)
      }
    }
  };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM