'How can I display the incremented value correctly based on user click?
I'm trying to replicate the likes system that Instagram for learning purposes. With my code below, the very first click on any photo increments correctly which means the likes amount is correctly being displayed as well as the specified user ID's DB column is correctly being incremented/decremented.
The problem I'm facing is: Whenever I try to like (click) any other photo after the first photo click (which correctly updates as mentioned above), I'll need to click it a few times, instead of once, in order for the likes value to change (increment/decrement) and even then, it's being displayed and updated incorrectly.
How can I make it so that the user can freely like/dislike (click once and click again on the same photo) any photo in any order so that the correct data is updated and displayed on the browser? Thanks in advance for any feedback.
Note: I'm using React Query to fetch DB data for each user.
Here's Gallery.js
const [currentUserClicks, setCurrentUserClicks] = useState(1);
async function fetchUploads() {
const headers = {
"Accept": 'application/json',
"Authorization": `Bearer ${authToken}`
};
const {data} = await axios.get('http://localhost/api/get-user-uploads-data', {headers});
return data;
}
const handleLikesBasedOnUserId = (likedPhotoUserId) => {
if(currentUserClicks > 1) {
setCurrentUserClicks(currentUserClicks - 1);
handleDisLike(likedPhotoUserId); // sends data to server to decrement DB column
} else {
setCurrentUserClicks(currentUserClicks + 1);
handleLike(likedPhotoUserId); // sends data to server to increment DB column
}
};
const handleLike = (likedPhotoUserId) => {
const url = 'http://localhost/api/like';
const headers = {
"Accept": 'application/json',
"Authorization": `Bearer ${authToken}`
};
let data = {
'UserID': likedPhotoUserId,
'likeCount': currentUserClicks
};
axios.post(url, data, {headers})
.then(resp => {
console.log("handleLike",resp.data.userLikes[0].likes);
}).catch(err => {
console.log(err);
});
};
const handleDisLike = (likedPhotoUserId) => {
const url = 'http://localhost/api/dislike';
const headers = {
"Accept": 'application/json',
"Authorization": `Bearer ${authToken}`
};
let data = {
'UserID': likedPhotoUserId,
'likeCount': currentUserClicks
};
axios.post(url, data, {headers})
.then(resp => {
console.log("handleDisLike", resp.data.userLikes[0].likes);
}).catch(err => {
console.log(err);
});
};
const { data } = useQuery('uploads', fetchUploads);
return(
<div className="main">
<ul className="cards">
{
data.map((photos, index) => {
return <Grid
src={photos.url}
likes={photos.likes}
currentUserClicks={currentUserClicks}
key={index}
onClick={handleLikesBasedOnUserId}
userId={photos.UserID}
/>
})
}
</ul>
</div>
);
Here's Grid.js:
const Grid = (props) => {
const [likes, setLikes] = useState(props.likes);
return (
<>
<img src={props.src} alt="Photo" className="gallery-img" onClick={() => props.onClick(props.userId, props.currentUserClicks > 1 ? setLikes(props.likes) : setLikes(props.likes + 1))}/>
<span style={{display: 'none'}}>{props.currentUserClicks}</span>
<h5 className="likes">Likes: {likes}</h5>
</>
);
}
Solution 1:[1]
if you are using react-query, then I don't see the need of having currentUserClicks in local state. Usually, it would go like this:
- You have a
querythat fetches photo data, including likes. - When the user clicks the like button, you make a
mutationto update the state on the backend - When the mutation is successful, you invalidate the query, which will re-fetch the photo query and thus show the correct state.
You can make the whole process snappier by using optimistic updates, where you would locally update the react-query cache via queryClient.setQueryData before you fire off the mutation. That makes sure the like button instantly reacts to the user interaction.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | TkDodo |
