Create a Comment Box and Comment Posting Function Using React JS

Jessica Lee
CodeX
Published in
7 min readApr 2, 2021

After finishing the Instagram clone project using vanilla JS, and now I am on track learning the basics of React and changing the vanilla JS codes that I wrote into React codes. The most important concepts of React are States and Props.

It took a lot of hassles just to understand what they mean and do. I am not even really comfortable with vanilla JS yet, but now that I have to learn React, it was just so much going on in my head.

Anyways, the main topic about this post is how to create a comment box with comment posting function using React. This post only refers to the UI, not considering the back-end. I am going to cover:

  • How to set states for comment values input by the user
  • How to enable ‘Post’ button on the comment box
  • How to work with states and props, and post comments to the comment list
  • Why applying a unique key to each comment is important

How to set states for comment values entered by the user & enable ‘Post’ button in the comment box

So, on the Instagram feeds, when nothing is entered in the comment input box, the ‘Post’ button is disabled (unable to click) and only when something is entered, the button gets enabled and turns blue. Please refer to the images below.

Default
Post Button Enabled

Let’s look at the codes below:

...
class Main extends Component {
constructor() {
super();
this.state = {
commentValue: “”,
commentLine: [{ commentId:””, text: “”, }],
};
}
...
  1. We need to start by setting the initial states of the component in the states object. We are on the Main component now, but eventually we are going to pass the states to the child component, CommentBox, through props. I am using class here, but you can also use function.
  2. I set the initial commentValue (the values entered in the input element)’s state empty and it will be changed when something gets entered. We will talk about the commentLine state later in the post.
handleCommentValue = (e) => {this.setState({
commentValue: e.target.value,
});
};

3. Now, we need to change the state of commentValueto what the user enters in the input element. This case is where we need an event handler function. I named it ashandleCommentValue and when the e(event) happens, setState() function gets executed. setState() changes the state to the assigned value which is e.target.value — the value that gets entered by the user.

render() {
return (
<>
...
<CommentBox
commentValue={this.state.commentValue}
handleCommentValue={this.handleCommentValue}

enterCommentLine={this.enterCommentLine}
submitCommentLine={this.submitCommentLine}
/>
...
</>

4. Then, we need to pass the states and event handler function down to the child component where has the comment box elements. As the CommentBox is a component, it can be returned in the parent component by writing <CommentBox />. Inside the tag, the state and event handler function can be passed down by writing them like attributes — such as commentValue={this.state.commentValue}. The names of attributes can be what you’d like, but I set them the same with the states’ name. We will get back to the last two states (enterCommentLine and submitCommentLine) later.

class CommentBox extends Component {render() { const { commentValue, handleCommentValue, 
enterCommentLine, submitCommentLine} = this.props;
const enableCommentButton = () => {
return (commentValue ? false : true);
}
const changeCommentButtonStyle = () => {
return (commentValue ? "comments-button-enabled" :
"comments-button-disabled");
}
return ( <div className="comments-box">
<input onKeyPress={enterCommentLine} value={commentValue}
id="comments-input" onChange={handleCommentValue}
type="text" placeholder="Add a comment..." />
<button onClick={submitCommentLine} type="submit"
className="comments-button"id={changeCommentButtonStyle()}
disabled={enableCommentButton()}>Post</button>
</div>
)}
}

5. Let’s focus on the bolded codes for now. From #4, we passed down the commentValue and handleCommentValue to CommentBox component. To use them in the component’s elements, we need to set them as props by writing const { commentValue, handleCommentValue} = this.props;.

6. Note that the handleCommentValue is assigned to onChange attribute, which tracks the values entered by the user in the element and set the commentValue to the values. Also, commentValue is assigned to value attribute to reset the input element when we submit/post the comment.

7. Inside render() but outside of return(), two functions are declared — enableCommentButton() to get rid of the disabled attribute from the Post button element, and changeCommentsButtonStyle() to change the style of the button when it gets enabled. Then, the two functions are inserted to the button tag — id={changeCommentButtonStyle()} disabled={enableCommentButton()} . Note the functions are wrapped with {} where you can write JS syntax in JSX. By applying the function to id, we can work with CSS to apply the appropriate style.

How to work with states and props, and post a comment to the comment list

Now, we need to post the comment by pressing Enter or clicking the Post button. I created a Comment component which represents each comment (commentLine) submitted by the user. But first, let’s continue working with the parent Main component and then CommentBox component. Now, the parent Main component has the state of commentLine.

this.state = {
commentValue: “”,
commentLine: [{ commentId:””, text: “”, }],
};
  1. Note that the commentLine is an array which contains each element of an object that has commentId and text data. We are going to talk about the commentId in the next section.
setCommentLine = () => {this.setState({
commentLine: [
this.state.commentLine,
{ commentId: commentCounter++, text: this.state.commentValue }],
commentValue: “”,
});

2. Continued in the parent Main component, I declared the setCommentLinefunction that sets the commentLine state. When we think about this state, it needs to change to whatever entered in the input element. So, thecommentValue state is assigned to text. After the commentLine gets posted, it should be empty — commentLine: "". Now let’s make two more functions that execute setCommentLine.

submitCommentLine = (e) => {
e.preventDefault();
this.setCommentLine();
};
enterCommentLine = (e) => {
if (e.charCode === 13) {
this.setCommentLine();
}
};

3. setCommentLine function needs to be executed when we post the comment by Enter or clicking the Post button. So, we need two even handler functions, one for clicking the button and the other one for pressing Enter. Inside each function, setCommentLine function is executed. These two functions are then again passed down to the child CommentBox component.

...
return
(
<div className="comments-box">
<input onKeyPress={enterCommentLine} value={commentValue}
id="comments-input" onChange={handleCommentValue}
type="text" placeholder="Add a comment..." />
<button onClick={submitCommentLine} type="submit"
className="comments-button"id={changeCommentButtonStyle()}
disabled={enableCommentButton()}>Post</button>
</div>
)}
}

4. In the child CommentBox component, note that enterCommentLine is assigned to onKeyPress attribute of the input element, and submitCommentLine is assigned to onClick attribute of the button element.

Why applying a unique key to each comment is important

Now, let’s look at each comment element that gets added as the user posts the comment.

let commentCounter = 1;class Main extends Component {constructor() {
super();
this.state = {
commentValue: “”,
commentLine: [{ commentId:””, text: “”, }],
};
}
....setCommentLine = () => {this.setState({
commentLine: [
this.state.commentLine,
{ commentId: commentCounter++, text: this.state.commentValue }],
commentValue: “”,
});
  1. Before we move onto the child Comment component, let’s recall that commentLine has an object element with commentId and text properties. commentId is a unique key given to each comment. Also, note that the commentCounter variable is declared in a global scope, which is used in setState() to assign commentId a unique number.
class Comment extends Component {render() {
const { commentLine } = this.props;
return (
<ul className=”comments-list”>
{commentLine.map((val) => {return
<li className=”each-comment”
key={val.commentId}>{val.text}
</li>

})
}
</ul>
)};
}

2. Now we are on the child Comment component. It needs to return the li element with the data using props from the parent component. Since the commentLine is an array with object elements that have two properties, we can use map() method to work with and return each property. Here, commendId is assigned to key of the li element, and text to the value.

So why is it important to give a unique key to the elements in an array?

We use key to give the elements in an array a stable identity. The stable identity helps to track which item is changed, added, or deleted. When React is recursing on the children of a DOM node, it iterates both lists of children at the same time and only mutates whenever there’s a difference. This means, if a new and different li element goes into the beginning of other li elements that have the same values from each other, it can cause a problem because React will mutate every child as if the elements are all different. More detailed explanation is here:

So, we need to give a unique key to the elements in an array to avoid inefficiency and possible issues with your application. I used the commentCounter which increases by 1 for each element, but this cannot be an ideal way when the component is to be used over and over in your app. Ideally, the unique ids for key are to be given from the back-end, or you can also utilize libraries that generate random numbers.

--

--