MERN app: Axios problem updating comments list

MERN app: Axios problem updating comments list
0.0 0

#1

Here’s the error output after after submitting a post to display updated comments list:

TypeError: this.props.data.map is not a function
CommentList.render
src/CommentList.js:7
   4 | 
   5 | class CommentList extends Component {
   6 |     render() {
>  7 |         let commentNodes = this.props.data.map(comment => {
   8 |             return (
   9 |                 <Comment
  10 |                     author={comment.author}```

(anonymous function)
src/CommentBox.js:34
  31 | 
  32 | axios.post(this.props.url, comment)
  33 | .then(res => {
> 34 |     this.setState({ data: res });
  35 | })
  36 | .catch(err => {
  37 |     console.log('err', err);

#2

I put a console.log() in and it traces out the poll every 2 seconds with the MongoDB collection output as an array. That is working as intended BUT stops polling after a post has just been made.


#3

I usually get that error when the array variable (data in your case) is undefined. Also , is data coming in as prop ? It looks like it is being set as a state


#4

Yes it’s loading the data from axios.get(this.props.url) and then immediately set the state to populate the list, which works as intended on first load:

componentDidMount() {
	this.loadCommentsFromServer();
	setInterval(this.loadCommentsFromServer, this.props.pollInterval);
}
loadCommentsFromServer() {
	axios.get(this.props.url)
	.then(res => {
		console.log(res.data);
		this.setState({ data: res.data });
	});
}

#5

Ok but shouldn’t your map function be then be like this.state.data.map? Or are you passing data as a prop to another function ? Assuming you have data declared in your constructor’s state


#6

This might be a dumb question, but is the component that’s doing the loading getting unmounted after a post is made? Maybe posting a link to a github repo could help, since there seem to be several different components in play here.


#7

There is a map function but I am handling that within the commentList component, based on the tutorial I built it from.


#8

Here is my Github repo for the project: https://github.com/japickering/mern


#9

Two problems, I think.

First, you’re setting an interval without clearing when the component is getting unmounted. This is leading to some unhelpful errors.

You might do something like this:

  componentDidMount() {
    this.loadCommentsFromServer();
    this.intervalId = setInterval(
      this.loadCommentsFromServer,
      this.props.pollInterval
    );
  }

  componentWillUnmount() {
    clearInterval(this.intervalId);
  }

Next, here:

    axios
      .post(this.props.url, comment)
      .then(res => {
        this.setState({ data: res });
      })
      .catch(err => {
        console.log("err", err);
      });

The response you’re getting from this POST request is not an array that you can the iterate through. Instead, it’s {"message":"Comment successfully added!"}, so don’t set your state here. You can check the network tab in Chrome to confirm.


#10

@sa-mm That’s very helpful thanks. The first part is very similar to the basic idea behind setting intervals and clearing them in vanilla JS. It’s just wrapped within component lifecycle hooks.

I found the problem area in commentBox.js, the author had called setState and passed this.state.data inside the catch error block…

handleCommentSubmit(comment) {
	let comments = this.state.data;
	comment.id = Date.now();
	let newComments = comments.concat([comment]);
	this.setState({ data: newComments });

	axios.post(this.props.url, comment)
	.catch(err => {
		console.log('err', err);
		this.setState({ data: comments });
	});
}

#11

No problem.

Ah, I see. I guess the idea was to set data to newComments, but overwrite that if there happened to be some problem with the api call.

Doing something like

handleCommentSubmit(comment) {
	let comments = this.state.data;
	comment.id = Date.now();

  axios.post(this.props.url, comment)
  .then(resp => {
    const newComments = comments.concat([comment]);
    this.setState({ data: newComments });
  })
	.catch(err => {
		console.log('err', err);
		this.setState({ data: comments });
	});
}

…might be a way to combine your solution with the author’s. I feel like the author’s original solution might lead to some flickering if the post request does return an error.

Also, what happens if that catch block does get called? Does the user’s comment just disappear?


#12

@sa-mm Thanks, I don’t think the 2nd line in the catch error block should be there. It is just providing a fallback in case there’s some error with the user input. I could do with being able to add form field validation in a React way.


#13

I think that’ll get called if there’s a network problem as well. But things look good!