0

hey guys im learning the useReducer hook and for the most part it seems to be quite similar to redux (minus the action being sent to the store etc)

the thing i seem to ALWAYS have problems with when i get more complex state management situations is trying to alter my state in the ways i would like to. in my code i am essentially trying to have a user select a track and add it to a list of favorite songs. my code seems to be replacing the state and not adding to it here is my initial state and my useReducer and then lastly my add function (which when a button is pressed down below it sends in a track to be added to the list

const initialState = {
  name:'',
  duration:''
};

const [favorites, dispatch]=useReducer(reducer, initialState)



const add=(song)=>{
  dispatch({type:'ADD', payload:song})
  console.log(favorites)
}

THIS is the part that is confusing me. in my reducer i have this

export const reducer=(song, action)=>{
  switch(action.type){
    case 'ADD':
    return {...song, name:action.payload}
}

WHICH is essentially adding a new object everytime called name: trackname BUT i do not want to overwrite the last item. i feel like i am using spread wrong and also returning the incorrect payload maybe?

my final state keeps looking like this

{name: "deep end", duration: ""}
when i want it to look something like this
``
[{name: "deep end", duration: ""}
{name: "ok", duration: ""}
{name: "testtrack", duration: ""}
]`

i have tried setting the initial state to somethingm like this

const initialState = {
  favorites:{
[ADD TRACKS HERE]
}
};

BUT CANT seem to overwrite the state correctly so that it ADDS to the array. it keeps overwritting the last one

Robert O'Toole
  • 197
  • 1
  • 11
  • 1
    Check out the guide on immutable update patterns: https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns you want to be using either concat or spread syntax to update the one property of state which is an array. Or just make the array be the whole state! – Linda Paiste Mar 23 '21 at 09:12
  • 1
    yeah man that was my problem. immutablity and deep copies. have to get used to that! my initial sate had an object that was essentially an array of objects. needed to do something like this in the reducer return {...song, favorites:[...song.favorites, action.payload]} that deep nested immutable stuff is new to me but that cleared up a lot! thanks man – Robert O'Toole Mar 23 '21 at 09:28

1 Answers1

1

Redux's guide to Immutable Update Patterns is a great resource on how to update nested data in a way that doesn't mutate your state.

With an array there are two main ways to immutably add an element.

  1. With spread syntax:
const newArray = [...songs, newSong];
  1. With .concat(), which returns a new array that contains the additional items (that is different from .push() which mutates the array and just returns the length).
const newArray = songs.concat(newSong);

You can decide what you want the shape of your state to be. Storing the array to a property favorites is fine, but adds another layer of complexity to your updates.

export const reducer = (state, action) => {
  switch (action.type) {
    case "ADD":
      return { 
        ...state, 
        favorites: [...state.favorites, action.payload]
      };
    default:
      return state;
  }
};

const initialState = {
  favorites: [] // initial state has an empty array
};

const [state, dispatch] = useReducer(reducer, initialState);

// here favorites is just a property of state, not the whole state
const favorites = state.favorites;

I would recommend that that state should just be the array of favorites itself.

export const reducer = (favorites, action) => {
  switch (action.type) {
    case "ADD":
      return [...favorites, action.payload]
    default:
      return favorites;
  }
};

// favorites is the whole state
// initial state is an empty array
const [favorites, dispatch] = useReducer(reducer, []);

In either case, we are expecting the action.payload to be a complete song object, not just the name.

dispatch({ type: "ADD", payload: { name: "Deep End", duration: "3:22" } });

You could extract that into a helper function. In Redux terms we call this function an Action Creator.

const addFavorite = (name, duration) => ({
  type: "ADD",
  payload: { name, duration }
});

dispatch(addFavorite("Deep End", "3:22"));
Linda Paiste
  • 38,446
  • 6
  • 64
  • 102