GuerillaRadio GuerillaRadio - 1 year ago 79
React JSX Question

React comments component - Target newly added comment component in list and run function on only this one

I am looking to create a comment edit timer which triggers when a new comment is added. It will show an 'Edit' button for 60 seconds before the button is removed (similar to what The Verge do with their comments).

I have a

Comments
component which makes an API call after
componentDidMount()
and renders a list of
Comment
components (by adding comment objects into my
comment: []
state. I also have a
CommentForm
component which allows users to submit a new comment.

When a comment is successfully submitted the API call returns a complete comment object which I then prepend to the existing comment state array. I also update my
newCommentId
state with the new comment id and set my
startEditTimer
boolean state to true.

postCommentSuccess = (res) => {
const newArray = this.state.comments.slice(0);
newArray.splice(0, 0, res.data);
this.setState({
comments: newArray,
newCommentId: res.data.id,
startEditTimer: true,
});
}


I render the list of comments like this...

render() {
if (this.state.comments.length) {
commentsList = (this.state.comments.map((comment) => {
const { id } = comment;
return (
<Comment
key={id}
id={id}
newCommentId={this.state.newCommentId}
startEditTimer={this.state.startEditTimer}
/>
);
}));
}

return (
<ul className="comments-list">
{commentsList}
</ul>
);
}


In my
Comment
component I am checking to see if the
startEditTimer
prop is set to true and then running the
startEditTimer()
function.

componentWillReceiveProps(nextProps) {
if (nextProps.startEditTimer === true) {
this.startEditTimer();
}
}

startEditTimer = () => {
this.setState({ editTimer: 60 });
setInterval(this.countdown, 1000);
}

countdown = () => {
this.setState({ editTimer: this.state.editTimer -= 1 });
}


In my return function I am then showing/hiding the edit button like so:

render() {
return (
<li className="comment">
{this.props.id === this.props.newCommentId &&
this.state.editTimer > 0 &&
<button onClick={this.editReply} className="edit-btn">Edit ({this.state.editTimer})</button>
}
</li>
);
}


This works to an extent, the edit button does show on a new comment when it is posted, but the countdown timer does not last 60 seconds, instead it seems to be reduced by one every 0.5 seconds or so. I believe this could be because the
startEditTimer()
function is running multiple times when a new comment is added instead of just once so I believe I need a way of only running the function on the newly added comment.

Answer Source

Another approach is to just pass the created time to the Comment component. Then, in the Comment component you add a setInterval-function that checks every second if the time passed since the create time is greater than 60 seconds. Could look something like this:

// Commentlist component
render() {
  if (this.state.comments.length) {
    commentsList = (this.state.comments.map((comment) => {
      const { id, createdTime } = comment;
      return (
        <Comment
          key={id}
          id={id}
          createdTime={createdTime}
          newCommentId={this.state.newCommentId}
        />
      );
    }));
  }

  return (
    <ul className="comments-list">
      {commentsList}
    </ul>
  );
}

Then in the Comment component:

// Comment component
componentDidMount() {
    this.intervalChecker = setInterval(() => {
        if((Date.now() - this.props.createdDate)/1000 >= 60) {
            this.setState({ showEditButton: false})
            clearInterval(this.intervalChecker)
        }
    }, 1000)
}

render() {
  return (
    <li className="comment">
      {this.state.showEditButton && 
      <button onClick={this.editReply} className="edit-btn">Edit ({this.state.editTimer})</button>
      }
    </li>
  );
}

I left out some additional code that you will need to add but I hope you'll get the point im trying to make.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download