My component is set to display a list of books, as card thumbnails. Each item from the list of books is generated by this component.
Each Card has a favorites icon, when clicking it adds the book to favoriteTitles
array. By pressing again on the favorites icon it removes it from the list.
const Card = ({ title, history }) => {
const dispatch = useDispatch();
const { favoriteTitles } = useSelector(({ titles }) => titles);
const { id, name, thumbnail } = title;
const [favorite, setFavorite] = useState(favoriteTitles?.some(item => item.titleId === title.id));
const handleFavoriteClick = () => {
const isFavorite = favoriteTitles?.some(item => item.titleId === title.id);
if (isFavorite) {
dispatch(removeFavoriteTitle(title));
setFavorite(false);
} else {
dispatch(addFavoriteTitle(title));
setFavorite(true);
}
};
return (
<CardContainer>
<Thumbnail thumbnail={thumbnail} />
{name}
<FavesIcon isActive={favorite} onClick={handleFavoriteClick} />
</CardContainer>
);
};
The issue with this component is when you press once on FavesIcon
to add, and if you changed your mind and want to remove it and press right away again, the favoritesTitles
array still has the old value.
Let's suppose our current favorites list looks like this:
const favoritesTitles = [{titleId: 'book-1'}];
After pressing on favorites icon, the list in Redux gets updated:
const favoritesTitles = [{titleId: 'book-1'}, {titleId: 'book-2'}];
And if I press again to remove it, the favoritesTitles
array inside the component is still the old array with 1 item in it. But if I look in Redux the list updated and correct.
How component should get the updated Redux value?
Update
I have specific endpoints for each action, where I add or remove from favorites:
GET: /users/{userId}/favorites
- response list eg [{titleId: 'book-1'}, {titleId: 'book-2'}]
POST: /users/me/favorites/{titleId}
- empty response
DELETE: /users/me/favorites/{titleId}
- empty response
For each action when I add or remove items, on success request I dispatch the GET
action. Bellow are my actions:
export const getFavoriteTitles = userId =>
apiDefaultAction({
url: GET_FAVORITE_TITLES_URL(userId),
onSuccess: data => {
return {
type: 'GET_FAVORITE_TITLES_SUCCESS',
payload: data,
};
},
});
export const addFavoriteTitle = (userId, id) => (dispatch, getState) => {
return dispatch(
apiDefaultAction({
method: 'POST',
url: SET_FAVORITE_TITLES_URL,
data: {
titleId: id,
},
onSuccess: () => {
dispatch(getFavoriteTitles(userId));
return { type: 'SET_FAVORITE_TITLE_SUCCESS' };
},
})
);
};
My reducers are pretty straight forward, I'm not mutating any arrays. Since only GET
request is returning the list of array, I don't do any mutating in my reducers:
case 'GET_FAVORITE_TITLES_SUCCESS':
return {
...state,
favoriteTitles: action.payload,
};
case 'SET_FAVORITE_TITLE_SUCCESS':
return state;
case 'DELETE_FAVORITE_TITLE_SUCCESS':
return state;