Reputation: 113
I am currently trying to create an interactive comment section that allows users to comment, reply, and like/dislike the comments of others. I am currently stuck on the likes/upvote section, as I am unable to update the value of likes in my component.
Here is my Comment
component (which represents a single comment in the comment section):
import { useState } from "react";
const Comment = ({
upvotes,
image,
username,
isUser,
replies,
createdAt,
id,
upvoteMethod,
}) => {
const [score, setScore] = useState(upvotes);
return (
<div className="comment-container">
<div className="comment">
<div className="upvotes-section">
<div className="upvotes">
<img
id="upvote"
src="/images/icon-plus.svg"
onClick={() => {
upvoteMethod(id, "upvote");
}}
></img>
<h3>{score}</h3>
<img
id="downvote"
src="/images/icon-minus.svg"
onClick={upvoteMethod}
></img>
</div>
</div>
<div className="comment-side">
<div className="comment-header">
<div className="profile">
<img src={image}></img>
<h5>{username}</h5>
<h6 className="created-at">{createdAt}</h6>
</div>
<div className="options">
<img src={isUser ? "images/icon-delete.svg" : ""}></img>
<img src="/images/icon-reply.svg"></img>
</div>
</div>
</div>
</div>
{replies.map((r) => {
return (
<div className="replies-section">
<img src={isUser ? "images/icon-delete.svg" : ""}></img>
</div>
);
})}
</div>
);
};
export default Comment;
And here is the App.jsx
file, where I pass down the update function for my upvote counter:
import Comment from "./components/Comment";
import "./App.css";
import CommentArea from "./components/CommentArea";
import TextField from "./components/TextField";
import { useState } from "react";
function App() {
let displayedComments = [
{
id: 1,
content:
"Impressive! Though it seems the drag feature could be improved. But overall it looks incredible. You've nailed the design and the responsiveness at various breakpoints works really well.",
createdAt: "1 month ago",
score: 12,
user: {
image: {
png: "./images/avatars/image-amyrobson.png",
webp: "./images/avatars/image-amyrobson.webp",
},
username: "amyrobson",
},
replies: [],
},
{
id: 2,
content:
"Woah, your project looks awesome! How long have you been coding for? I'm still new, but think I want to dive into React as well soon. Perhaps you can give me an insight on where I can learn React? Thanks!",
createdAt: "2 weeks ago",
score: 5,
user: {
image: {
png: "./images/avatars/image-maxblagun.png",
webp: "./images/avatars/image-maxblagun.webp",
},
username: "maxblagun",
},
replies: [
{
id: 3,
content:
"If you're still new, I'd recommend focusing on the fundamentals of HTML, CSS, and JS before considering React. It's very tempting to jump ahead but lay a solid foundation first.",
createdAt: "1 week ago",
score: 4,
replyingTo: "maxblagun",
user: {
image: {
png: "./images/avatars/image-ramsesmiron.png",
webp: "./images/avatars/image-ramsesmiron.webp",
},
username: "ramsesmiron",
},
},
{
id: 4,
content:
"I couldn't agree more with this. Everything moves so fast and it always seems like everyone knows the newest library/framework. But the fundamentals are what stay constant.",
createdAt: "2 days ago",
score: 2,
replyingTo: "ramsesmiron",
user: {
image: {
png: "./images/avatars/image-juliusomo.png",
webp: "./images/avatars/image-juliusomo.webp",
},
username: "juliusomo",
},
},
],
},
];
const [comments, setComment] = useState(displayedComments);
const updateComments = (event) => {
setComment(comments);
};
const upvotePost = (id, action) => {
for (let i = 0; i < comments.length; i++) {
if (comments[i].id == id) {
action === "upvote" ? comments[i].score++ : comments[i]--;
setComment(comments);
console.log(action);
}
}
};
return (
<div className="App">
<CommentArea upvoteMethod={upvotePost} comments={comments}></CommentArea>
<TextField></TextField>
</div>
);
}
export default App;
When I try to console log the comments array after using setState()
, it shows that the value has updated after clicking the buttons, but it does not show on the UI.
for reference here is a screenshot of me debugging in the console (upvotes is comments.score
, and I clicked on the first comment/the one with 12 upvotes)
Can anyone help me understand what is going on and how to fix it? help would be appreciated.
Upvotes: 1
Views: 1443
Reputation: 50974
State updates in reactjs should be immutable, you can think of this as meaning that you shouldn't update/mutate your current state in any way, but rather, create a new state value and apply your changes to that. You're currently mutating your state and then setting your state back to the mutated state value:
action==="upvote" ? comments[i].score++ : comments[i]--
setComment(comments)
From React's perspective, the new comments
array that you're setting as your state is the same as your old comments
state (ie: if you were to compare the old state and the new state with ===
, you would get back true
), so React doesn't rerender as it can't see the state change. Instead, you should be creating a new comments array (below this is created with .map()
), and then creating new inner comment objects when you want to update the score
value:
const upvotePost=(id, action)=> {
const mult = action === "upvote" ? 1 : -1;
setComment(
comments => comments.map(comment => comment.id === id
? {...comment, score: comment.score + (1*mult)}
: comment
)
);
}
Notice that above we never update the comments
state directly, but instead return a new array, with a new object for the ones we want to update. The new object is created by using {...comment, score: comment.score + (1*mult)}
, where the ...comment
adds all of the (own enumerable) keys from the current object (see the spread syntax ...
), and then sets the score
key on the newly created object to an updated value. The arrow function in the setComments()
is being used to obtain the most up to date version of comments
before we update it - see functional updates.
Another issue is that you don't need your score
state in your Comment
component. The useState(upvotes)
sets the score
value to the value of upvotes
on the intial render of your component, but when the upvotes
prop changes, your score
state won't change with it. As you're not using setScore
, you can remove this state and instead use upvotes
directly.
Upvotes: 2