119

Everything works fine, but I have this warning Expected to return a value at the end of arrow function array-callback-return. I tried using forEach instead of map, but then <CommentItem /> doesn't even show. How do I fix this?

  return this.props.comments.map((comment) => {
  
      if (comment.hasComments === true) {
      
        return (
          <div key={comment.id}>
          
            <CommentItem className="MainComment"/>

              {this.props.comments.map(commentReply => {
              
                if (commentReply.replyTo === comment.id) { 
                  return (
                    <CommentItem className="SubComment"/>
                 ) // return
                } // if-statement
              }) // map-function
              } // map-function __begin
            
          </div> // comment.id
          
        ) // return
Max
  • 1,054
  • 1
  • 12
  • 20
Rami Chasygov
  • 2,714
  • 7
  • 25
  • 37
  • 1
    Is there another return if you do not go into the if? – epascarello Jul 10 '17 at 14:03
  • 25
    You only return if `commentReply.replyTo === comment.id`. If that's not the case, you don't return anything. Just put `return null` after the `if` block – Lennholm Jul 10 '17 at 14:04
  • Mikael Lennholm, may i say, you are genius – Rami Chasygov Jul 10 '17 at 14:11
  • @RamzanChasygov some languages don't allow single-branch `if` statements because they lead to problems like this. You'd be doing yourself a favor if you *always*, **always** write the `else` branch of any `if` statement – `if` represents a *fork* in your code, so you need to tell the program what happens on each path; not just one. – Mulan Jul 10 '17 at 14:40

7 Answers7

209

A map() creates an array, so a return is expected for all code paths (if/elses).

If you don't want an array or to return data, use forEach instead.

Zanon
  • 29,231
  • 20
  • 113
  • 126
118

The warning indicates that you're not returning something at the end of your map arrow function in every case.

A better approach to what you're trying to accomplish is first using a .filter and then a .map, like this:

this.props.comments
  .filter(commentReply => commentReply.replyTo === comment.id)
  .map((commentReply, idx) => <CommentItem key={idx} className="SubComment"/>);
Kris Selbekk
  • 7,438
  • 7
  • 46
  • 73
  • What if no match found to filter – CyberAbhay Mar 14 '19 at 11:25
  • 4
    If no comments matches the filter, an empty array will be returned. That will be passed to `.map`, which in turn will be a no-op. In other words - if there's no match, nothing will be rendered. – Kris Selbekk Mar 17 '19 at 11:43
  • As Zanon suggested below, an easier and less complex option is to simply use `forEach` that doesn't expect to return anything instead of `map` that do expect to return something. – Emanuel Lindström Jul 28 '20 at 15:05
35

The easiest way only if you don't need return something it'ts just return null

CrsCaballero
  • 2,124
  • 1
  • 24
  • 31
  • Just use forEach in that case as suggested by @Zanon – Shivam Jha Oct 20 '20 at 08:52
  • A good option for lists of React components, where it's better for reconciliation to see a consistent number of elements with some nulls than an entirely different list. – Noumenon Dec 02 '22 at 09:20
10

The problem seems to be that you are not returning something in the event that your first if-case is false.

The error you are getting states that your arrow function (comment) => { doesn't have a return statement. While it does for when your if-case is true, it does not return anything for when it's false.

return this.props.comments.map((comment) => {
  if (comment.hasComments === true) {
    return (
      <div key={comment.id}>
        <CommentItem className="MainComment" />
        {this.props.comments.map(commentReply => {
          if (commentReply.replyTo === comment.id) { 
            return (
              <CommentItem className="SubComment"/>
            )
          }
        })
        }
      </div>
    )
  } else {
     //return something here.
  }
});

edit you should take a look at Kris' answer for how to better implement what you are trying to do.

Chris
  • 57,622
  • 19
  • 111
  • 137
5

The most upvoted answer, from Kris Selbekk, it is totally right. It is important to highlight though that it takes a functional approach, you will be looping through the this.props.comments array twice, the second time(looping) it will most probable skip a few elements that where filtered, but in case no comment was filtered you will loop through the whole array twice. If performance is not a concern in you project that is totally fine. In case performance is important a guard clause would be more appropriated as you would loop the array only once:

return this.props.comments.map((comment) => {
  if (!comment.hasComments) return null; 

  return (
    <div key={comment.id}>         
      <CommentItem className="MainComment"/>
        {this.props.comments.map(commentReply => {             
          if (commentReply.replyTo !== comment.id) return null;

          return <CommentItem className="SubComment"/>
        })} 
    </div>          
  ) 
}

The main reason I'm pointing this out is because as a Junior Developer I did a lot of those mistakes(like looping the same array multiple times), so I thought i was worth mention it here.

PS: I would refactor your react component even more, as I'm not in favour of heavy logic in the html part of a JSX, but that is out of the topic of this question.

0

You can use the for loop like so:

for(let i = 0 ; i < comments.length; i++){
 if(comments[i].hasComments === true){
return (
       <div key={comments[i].id}>
        //content Here
      </div> // comment.id
        )
      }
     }
Sands QA
  • 7
  • 3
-2

class Blog extends Component{
 render(){
  const posts1 = this.props.posts;
  //console.log(posts)
  const sidebar = (
   <ul>
    {posts1.map((post) => {
     //Must use return to avoid this error.
          return(
      <li key={post.id}>
       {post.title} - {post.content}
      </li>
     )
    })
   }
   
   </ul>
  );
  const maincontent = this.props.posts.map((post) => {
   return(
    <div key={post.id}>
     <h3>{post.title}</h3>
     <p>{post.content}</p>
    </div>
   )
  })
  return(
   <div>{sidebar}<hr/>{maincontent}</div>
  );
 }
}
const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];

ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);
Srinivasan N
  • 733
  • 8
  • 9