Redux store doesn't update all changes in app.js

such an honor to drop my first question in this community! I'm working on a recipe app where I use Redux to manage states. I'm using async storage to store changes locally. I'm a bit stuck now because my store only applies and stores a few changes instead of the whole recipe.

This is how the data of a recipe looks like (sorry for my Dutch):

    {
  cardId: 2,
  time: "5 minutes",
  title: "Wortel-Kokossoep met Dadelroom",
  category: "ontbijt",
  image: require("./assets/wortel-kokossoep.jpg"),
  subtitle: "Gezonde en makkelijke soep!",
  caption: "Wortel-Kokossoep met Dadelroom",
  description:
    "Begin de dag gezond met deze smoothie dat rijk is aan vitamines.",
  stepOne: "Stap 1: Voeg alles toe aan de NutriBullet of blender.",
  stepTwo:
    "Stap 2: Blend twee keer gedurende ongeveer 5 tot 10 seconden en je bent klaar!",
  stepThree: "",
  stepFour: "",
  stepFive: "",
  stepSix: "",
  stepSeven: "",
  stepEight: "",
  favorite: false
},

and this is how I implemented Redux in the app.js. Please forgive me for posting the whole code. I'm still a noob, eager to learn everything about Redux and react.

    const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "FAV_RECIPIE":
      //const recipie = state.recipies.find(r => (r.cardId = action.id));
      const recipieIndex = state.recipies.findIndex(
        r => r.cardId === action.id
  );

  const currentValue = state.recipies[recipieIndex].favorite;
  state.recipies[recipieIndex].favorite = !currentValue;

  state.recipies = [...state.recipies];

  saveRecipes(state.recipies); // save to local storage
  return { ...state };

case "SET_LOADED_RECIPIES":
  console.warn("!!!!");
  if (action.recipies) {
    state.recipies = [...JSON.parse(action.recipies)]; // JSON parse to convert string back to list
  }
  console.log("set recipies");
  return { ...state };

case "OPEN_MENU":
  return { action: "openMenu" };
case "CLOSE_MENU":
  return { action: "closeMenu" };
default:
  return state;


 }
};

const saveRecipes = async recipies => {
  try {
    await AsyncStorage.setItem("@VV:Recipes", JSON.stringify(recipies)); // JSON stringify to convert list to string (for storage)
  } catch (error) {
    // error saving, and that is fine =)
    console.log("could not save recipes");



 }
};

const store = createStore(reducer, initialState);

store.subscribe(() => {
  console.log("store changed", store.getState().recipies);
});

const App = () => (
  <Provider store={store}>
    <AppNavigator />
  </Provider>
);

export default App;

I really hope some of you can help me out! Thanks in advance!

Upvotes: 0

Views: 132

Answers (1)

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15698

There's a couple of things going wrong in your reducer, but the big thing is doing state-mutations. You want to avoid logic like:

state.recipies[recipieIndex].favorite = !currentValue;

also

state.recipies = [...state.recipies];

This is against redux principles. You never want to directly change values of the state without first making a copy or clone.

So we will go with creating a shallow-copy of state in your reducer and make updates to that instead:

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "FAV_RECIPIE":
      var newState = {...state}
      //const recipie = state.recipies.find(r => (r.cardId = action.id));
      const recipieIndex = state.recipies.findIndex(
        r => r.cardId === action.id
      );

      const currentValue = state.recipies[recipieIndex].favorite;
      newState.recipies[recipieIndex].favorite = !currentValue;

      saveRecipes(newState.recipies); // save to local storage
      return { ...newState };

    case "SET_LOADED_RECIPIES":
      console.warn("!!!!");
      var newState = [...state]
      if (action.recipies) {
        newState.recipies = [...JSON.parse(action.recipies)]; // JSON parse to convert string back to list
      }
      console.log("set recipies");
      return { ...newState };

    case "OPEN_MENU":
      return { action: "openMenu" };
    case "CLOSE_MENU":
      return { action: "closeMenu" };
    default:
      return state;
  }
};

Alternatively we can handle this succinctly using .map() which creates a copy for us.

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "FAV_RECIPIE":
      const updatedRecipes = {
        ...state,
        recipes: state.recipes.map(recipe => {
          if (recipe.cardId === action.id) {
            return {
              ...recipe,
              favorite: !recipe.favorite
            };
          } else {
            return recipe;
          }
        })
      };
      saveRecipes(updatedRecipes)
      return {
        ...updatedRecipes
      }
    case "SET_LOADED_RECIPIES":
      var newState = {...state};
      if (action.recipies) {
        newState.recipies = [...JSON.parse(action.recipies)]; // JSON parse to convert string back to list
      }
      return { ...newState };

    case "OPEN_MENU":
      return { action: "openMenu" };
    case "CLOSE_MENU":
      return { action: "closeMenu" };
    default:
      return state;
  }
};

Upvotes: 1

Related Questions