skip to Main Content

I want the user to tap on an item from a list and add it to their favorites page. The problem I’m having is that if the user taps on the same item twice it will be added to their favorites page twice and create issues because two children have the same key.

I’m updating the state this way in reducer:

const initialState = {
  selectedFavorites: [],
};

addFavorite: (state, action) => {
  return {
    ...state,
    selectedFavorites: [...state.selectedFavorites, action.payload],
  };
},

When user taps on item to be added to favorites:

const handlePress = () => {
  favorite === true ? setFavorite(false) : setFavorite(true);

  dispatch(
    addFavorite({
      id: currentIndex,
      title: getIds[currentIndex].title,
      liked: 'true',
    }),
  );
  setTimeout(() => {
    setFavorite(false);
  }, 500);
};

I’ve tried using new Set() but was unable to make this work. What is a good way to copy and render the state without any duplicate values?

3

Answers


  1. Instead of fixing this in the reducer, I’d fix it before the dispatch, for example, check if it’s already a favourite.

    Assuming ids contains a list of all favourite indexes, use includes to check if this ID already exists in the array

    if (ids.includes(currentIndex)) {
       // Show warning that this is already a fav, for example:
       setWarning("You've already added this to your favorites!");
    } else {
        dispatch(
          addFavorite({
            id: currentIndex,
            title: getIds[currentIndex].title,
            liked: 'true',
          }),
        );
    }
    
    Login or Signup to reply.
  2. If what you want is way of filteing only distinct values of a list, this solution, written by @TLindig.

    But you also could check if the item is already favorited before calling dispatch:

    if(isFavorited(currentIndex))
        return;
    dispatch(
            addFavorite({
            id: currentIndex,
            title: getIds[currentIndex].title,
            liked: 'true',
        }),
    );
    setTimeout(() => {
        setFavorite(false);
        }, 500);
    };
    
    Login or Signup to reply.
  3. In the addFavorite reducer function check if the selectedFavorites array already contains an element with matching id value, and only add new favorites.

    Example:

    addFavorite: (state, action) => {
      // Check if favorites already includes the item
      if (state.selectedFavorites.some(el => el.id === action.payload.id)) {
        return state;
      }
    
      // Not included, add to favorites
      return {
        ...state,
        selectedFavorites: [...state.selectedFavorites, action.payload],
      };
    },
    

    Alternatively it appears you could fix this in the handler since it seems you also keep and maintain a local favorite state.

    Example:

    const handlePress = () => {
      // Toggle favorite state
      setFavorite(favorite => !favorite);;
    
      // If not already favorited, dispatch action and timeout
      if (!favorite) {
        dispatch(
          addFavorite({
            id: currentIndex,
            title: getIds[currentIndex].title,
            liked: 'true',
          }),
        );
        setTimeout(() => {
          setFavorite(false);
        }, 500);
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search