I am creating app for bookmarking marvel characters (heroes), so when I click on specific bookmark icon it should change into filled bookmark icon and vice-versa. Unfortunately when I click on single one all icons change. I guess the problem is in the state. Please help!
As a prop I am receiving all data about characters (image, name) Here is the code:
import React, { useState, useEffect } from "react";
import "./CharactersList.css";
import { RiBookmarkLine } from "react-icons/ri";
import { RiBookmarkFill } from "react-icons/ri";
const CharactersList = (props) => {
const [isLiked, setisLiked] = useState(false);
const [toggle, setToggle] = useState(false);
const [favourites, setFavourites] = useState([]);
useEffect(() => {
const bookMarkedData = JSON.parse(
localStorage.getItem("react-bookmarked-characters")
);
if (bookMarkedData) {
setFavourites(bookMarkedData);
}
}, []);
const saveToLocalStorage = (data) => {
localStorage.setItem("react-bookmarked-characters", JSON.stringify(data));
};
const handleBookmarking = (character) => {
if (toggle === true) {
const newBookmarkedList = [...favourites, character];
setFavourites(newBookmarkedList);
saveToLocalStorage(newBookmarkedList);
setisLiked(true);
} else {
const newBookmarkedList = favourites.filter(
(favourites) => favourites.id !== character.id
);
setFavourites(newBookmarkedList);
saveToLocalStorage(newBookmarkedList);
setisLiked(false);
}
};
const toggleLike = () => {
setisLiked(!isLiked);
Bookmarked(isLiked);
};
const Bookmarked = () => {
return isLiked ? <RiBookmarkFill /> : <RiBookmarkLine />;
};
return (
<>
{props.characters.map((character, index) => (
<div className="card-container" key={index}>
<img
src={`${character.thumbnail.path}/standard_fantastic.${character.thumbnail.extension}`}
alt="character"
className="image"
/>
<div className="info-container">
<p className="name">{character.name}</p>
<div
className="icon"
onClick={() => {
setToggle(!toggle);
handleBookmarking(character);
}}
>
<Bookmarked />
</div>
</div>
</div>
))}
</>
);
};
export default CharactersList;
Problem is in the state you are right. You are storing one boolean value for all your heroes. You should change the state value to an array of ids instead of one boolean and in this array, you store ids of bookmarked characters. So your handleBookmarking function should be something like this:
const handleBookmarking = (character) => {
if (isLiked.includes(character.id)) {
const newIsLiked = [...isLiked];
newIsLiked.filter(id => id === character.id);
setIsLiked(newIsLiked);
} else {
setIsLiked([...isLiked, character.id]);
}
}
With this implementation, you no longer need toggle state. If your character's id is in the array, it's bookmarked and if it's not, it's not bookmarked. If your character doesn't have an id, you can use name or any other prop that is unique. You will also have to change your BookMarked component implementation to something like this:
//implementation of BookMarked
const Bookmarked = ({id}) => {
return isLiked.includes(id) ? <RiBookmarkFill /> : <RiBookmarkLine />;
};
And also change it in return statement:
//from this:
<Bookmarked />
//to this:
<Bookmarked id={character.id} />