Fat Arrow Function Clarification in React

#1

Hi,

I’m going through a React course by Mosh Hamedani and I’ve hit and issue as I clearly don’t understand how arrow functions are constructed. So I’ve been trying to deconstruct it and I’ve started to realise that my issue is that I’m probably not understanding how parameters are handled in JavaScript properly and I’m missing a pretty large piece of the whole puzzle. I think if I can get a clear explanation, even if it’s to say ‘you need to look back at…’.

Basically the code block is the:

  handleDelete = movie => {
    const movies = this.state.movies.filter(m => m._id !== movie._id);
    this.setState({ movies });
  };

So this filters the movies and creates a new array and updates the movies state. The function creates a new array of the movies but filters the movie object itself. I think this is correct, although I’m not quite sure why. Why do we have to filter the movie object? We just want the movie ID’s but why would we need a filter to do this? So that’s the first part of clarification.

The second part which will no doubt dovetail with the first part is this:

(m => m._id !== movie._id);

So in non tech parlance : the m parameter is going to m._id and not movie._id

I think the m is referencing values in the functions declared another JS file, but if the m is changed to let’s say x it returns an error ‘undefined’ so where is that m declared, and how?

I think people may need all the code. so let me know, but here is a screen shot of it referenced in the other external file:

If someone could point me in the right direction, and be completely honest and brutal about what I’ve missed in terms of helping me understand.

Regards

0 Likes

#2

You are intending to delete a movie, so you are filtering for everything that doesn’t match the movie being deleted.

It’s this.state.movies.filter that results in the m variable. this.state.movies is a list where each item is a movie. The filter method passes each item (that being a movie) to the function. The function then receives that movie as the first parameter of the function. In this case, it is m that has been decided to refer to each movie in the list.

Let’s start with code that doesn’t use fat arrows.

function handleDelete(movie) {
    function movieFilter(m) {
        return m._id !== movie._id;
    }
    const movies = this.state.movies.filter(movieFilter);
    return this.setState({ movies });
}

The above functions are normal and standard function declarations. They can also be assigned to variables, where a function expression is used for that instead.

Here is the same code using function expressions. I’ve commented out the previous code, to help show how the changes occur.

// function handleDelete(movie) {
const handleDelete = function handleDelete(movie) {
    // function movieFilter(m) {
    const movieFilter = function movieFilter(m) {
        return m._id !== movie._id;
    // }
    };
    const movies = this.state.movies.filter(movieFilter);
    return this.setState({ movies });
// }
};

With function expressions, the variable and the function name can be different. Inside of the function you can refer to the function both by the function name, and the variable that you assigned the function to.

Some people prefer to make them anonymous functions by removing the function name, which removes the clutter of doubling up the function names. I have a personal and historical dislike for anonymous functions though, because when debugging it can be difficult to to see where you are in a trace report when using anonymous functions.

Still, here’s the same code with anonymous functions:

// const handleDelete = function handleDelete(movie) {
const handleDelete = function (movie) {
    // const movieFilter = function movieFilter(m) {
    const movieFilter = function (m) {
        return m._id !== movie._id;
    };
    const movies = this.state.movies.filter(movieFilter);
    return this.setState({ movies });
};

You can also use functions directly as function arguments. Terms like arguments and parameters tend to get bandied about a lot, and they have specific meanings.

The following code helps to clear up that confusion and helps to give a clear understanding about how arguments and parameters differ.

function sum(parameter1, parameter2) {
    return parameter1 + parameter2;
}
const argument1 = 3;
const argument2 = 5;
sum(argument1, argument2); // 8

Parameters are defined as part of the function declaration or function expression.
When you execute or call the function, arguments are what you give to the function, which are then received by that function as the parameters.

The movieFilter function can also be given as an argument directly to the filter function. That means that we don’t need to assign it as a variable. The function can either be named or anonymous too. I prefer to use named functions, as that can help us to better understand what the function is supposed to be doing.

const handleDelete = function (movie) {
    // const movieFilter = function (m) {
    //     return m._id !== movie._id;
    // };
    // const movies = this.state.movies.filter(movieFilter);
    const movies = this.state.movies.filter(function filterMovie(m) {
        return m._id !== movie._id;
    });
    return this.setState({ movies });
};

You can also use arrow-notation with the filterMovie variable. The simplest way to convert a function to arrow-notation, is to remove the word function, place an arrow after the parameters, and remove the return keyword.

Here is some example code with the old filterMovie function commented out, and replaced with arrow-notation instead.

    // const movieFilter = function (m) {
    //      return m._id !== movie._id;
    // };
    const movieFilter = (m) => {
         m._id !== movie._id);
    };

While arrow-notation with multiple statements is supported by using the function braces, it’s preferred to use arrow-notation as a simple one-liner expression, by removing the function braces too.

    // const movieFilter = (m) => {
    //      m._id !== movie._id;
    // };
    const movieFilter = (m) => m._id !== movie._id;

When there’s only one function parameter, you can even remove the parenthesis around the parameter, but some style guides and linters, and myself, disagree with that. Still, here’s what it looks like without the parameter parenthesis.

    // const movieFilter = (m) => m._id !== movie._id;
    const movieFilter = m => m._id !== movie._id;

Finally, the movieFilter arrow-notation function can be given to the filter method as an argument to the filter function, and is how your original code uses the arrow-notation.

const handleDelete = function (movie) {
    // const movieFilter = m => m._id !== movie._id;
    // const movies = this.state.movies.filter(movieFilter);
    const movies = this.state.movies.filter(m => m._id !== movie._id);
    return this.setState({ movies });
};

If I had my way about things, I would try to improve the understandability of the code by extracting the arrow-function out to a separate variable, retain the function parenthesis, and slightly rename some variables for better clarity.

const handleDelete = function (movieToDelete) {
    const keepMovieFilter = (movie) => movie._id !== movieToDelete._id;
    const movies = this.state.movies.filter(keepMovieFilter);
    return this.setState({ movies });
};

I hope that helps to give a better understanding of how one type of style flows into another.

6 Likes

#3

Thanks so much, this is excellent, exactly what I was looking for.

I’m going to take this version of your code and just try and talk through it to see if I’ve understood. I hope that is okay.

Basically I have a list of movies and the movie which is ‘clicked’ is passed to a parameter which is called ‘movie’:

 <button onClick={() => this.handleDelete(movie)}
className="btn btn-danger btn-sm">
                    Delete
                  </button>

Then we need to handle the delete and update the list. So here I’m going to use your code:

const handleDelete = function (movieToDelete) {
    const keepMovieFilter = (movie) => movie._id !== movieToDelete._id;
    const movies = this.state.movies.filter(keepMovieFilter);
    return this.setState({ movies });
};

So here the movieToDelete parameter is being passed the movie that the user is currently deleting. What we are doing is storing all the movies in a const called keepMovieFilter the movie object (the list of movies) BESIDES the one being deleted.

I think at this point I’m still a bit unsure as to this part, despite your explanation:

(movie) => movie._id

I think here I’m saving the list of movies from the ids in a ‘movie’ parameter then passing them to keepMovieFilter const - so could that parameter there be named something else? That could justs as well be:

(y) => y._id

Then finally the state is being updated which I can follow!

Is that close to the correct interpretation?

1 Like

#4

Close.

The keepMovieFilter variable does the same as the following function:

function keepMovieFilter(movie) {
    return movie._id !== movieToDelete._id;
}

It just returns true or false depending on if a movie id matches the one that we want to delete.

That can also be defined as a function expression:

const keepMovieFilter = function (movie) {
    return movie._id !== movieToDelete._id;
};

Or in shorthand by using arrow-notation like this:

const keepMovieFilter = (movie) => {
    return movie._id !== movieToDelete._id;
};

or as

const keepMovieFilter = (movie) => movie._id !== movieToDelete._id;

or as

const keepMovieFilter = movie => movie._id !== movieToDelete._id;

All of the above do the same thing.

/me casts a pedant-aware eye at people with statements that there are differences about the this keyword and other things that aren’t related to what’s happening here.

0 Likes

#5

Thanks Paul,

So here:

function keepMovieFilter(movie) {
    return movie._id !== movieToDelete._id;
}

So that statement is actually always false until a movie is deleted in which case it returns true, then what is happening programmatically, it’s invoking the keepMovieFilter function?

I actually think my issue is not how it’s written but what the function is actually doing.

Chris

0 Likes