-1

Im writing a react application and while trying to get all warnings out I found a weird bug...

handleLike = id => {
const movies = [...this.state.movies];
const movie = movies.filter(obj => obj._id === id);
if (movie.map(obj => obj.liked) == "fa fa-heart-o") {
  movie.map(obj => (obj.liked = "fa fa-heart"));
  this.setState({ movies });
} else {
  movie.map(obj => (obj.liked = "fa fa-heart-o"));
  this.setState({ movies });
}

In if (movie.map(obj => obj.liked) == "fa fa-heart-o") I dont type check with (===) because it will be falsy for some reason, but obj.liked after Console Logging it with typeof, it says it is a String, so it definetly should be truthy, even after I added ToString() it did not get truthy... I double checked everything, am I missing something ?

Thanks in Advance!

Bennity
  • 213
  • 1
  • 3
  • 10
  • 5
    `movie.map(obj => obj.liked)` will return an _array_, not a string. It might help your question if you explain what it is you're trying to do with that code. – Andy Mar 18 '20 at 09:37
  • I check if an Object is already liked and if it is liked i will render a full heart, otherwise I will render an empty heart, the code is working like it is, but I tried to get all warnings out of my code. – Bennity Mar 18 '20 at 09:45
  • 1
    @Bennity if it works, it means that that case your `filter` should probably be `find` as you get a single object back. When you have an array with one item and compare it to a string `==` will convert the array to a string. – VLAZ Mar 18 '20 at 09:58

1 Answers1

1

You're doing a lot of unnessary mapping.

1) movies.filter(obj => obj._id === id); will return an array of one element which is a movie object. To grab the actual movie object you need to use movies.filter(obj => obj._id === id)[0]; instead. You might also want to consider using find (to grab the movie object) or findIndex to identify the index of the movie you want.

2) if (movie.map(obj => obj.liked) == "fa fa-heart-o") makes no sense - you're trying to compare an array (map will always return a new array) to a string. I'm surprised that works at all.

Based on your comment I might rewrite your code as follows:

handleLike = id => {

  const movies = [...this.state.movies];

  // Find the index of the movie where `_id` matches `id`
  const movie = movies.find(obj => obj._id === id);

  // If the movie at the index is liked (either true/false),
  // give the object a new property "icon" and give it a heart
  if (movie.liked) {
    movie.icon = "fa fa-heart";
  } else {

    // Otherwise give it an empty heart
    movie.icon = "fa fa-heart-o";
  }

  // Set the new state (only once)
  this.setState({ movies });

}
Andy
  • 61,948
  • 13
  • 68
  • 95
  • 1
    It works now! Thanks for the good explanation, it is really a lot of unnessary mapping. – Bennity Mar 18 '20 at 10:12
  • 1
    You could also change that `if` statement to a ternary: `movie.icon = movie.liked ? 'fa fa-heart' : 'fa fa-heart-o'`, but I prefer the longer method when I'm explaining things. @Bennity – Andy Mar 18 '20 at 10:22
  • I just started to learn hooks, how would I write this in hooks? – Bennity Mar 19 '20 at 13:58
  • 1
    All you'd need to do is understand how `useState` works, and instead of `this.setState({ movies })` you'd call your state function instead. So something like `const [ movies, setMovies ] = useState([]);` then in your handler remove the line `const movies = [...this.state.movies]` because you'll already be accessing the `movies` state, and at the end do `setMovies(movies)` once you've done the update. – Andy Mar 19 '20 at 14:47