Pass Data To Event Handlers with Partial Function Application

Andy Van Slaars
InstructorAndy Van Slaars
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated 6 years ago

In this lesson we’ll see how to pass an item’s id value in an event handler and get the state to reflect our change. We’ll also create a helper function that allows us to use partial function application to clean up the event handler code and make it more “functional”

[00:00] We have helper functions to find. For finding toDo by ID, toggling the is complete flag of toDo, and taking an updated toDo and replacing a previous version of it in an array. Let's combine these and wire them up to handle marking toDos as complete in our app.

[00:15] I'll start by opening Add.js. At the top of the file, I'll take the import statement to also import FindByID, title "toDo," and Update toDo. I'm going to drop down into the component right below my initial state and I'm going to define HandleToggle, which is going to be an error function that takes an ID as the argument.

[00:41] I'm going to take that ID and use it to get my toDo. For that, I'll use FindByID. I'll parse it in the ID argument and file by this.state.toDos. I'm going to get a toggled version of that toDo. We'll call that "Toggled." I'm going to set that to equal Call to Toggle toDo, parsing in the toDo that I retrieved by ID.

[01:06] I want to define an updated list of toDos, we'll call "UpdatedtoDos." I'm going to set to equal a call UpdatetoDo. That's going to accept our existing list this.state.toDos and the updated item, which in this case is Toggled. I'm going to use that updated toDo list to redefine the toDos in our state.

[01:30] We'll do that by calling this .setState. We'll parse that an object. That object will have a toDos key with the value updated to toDos. Now, for the state updates to happen, we need to parse this method down as a prop to the toDo item component.

[01:47] I'm going to scroll down to the .jsx. ToDo item is a child of the toDo list. We want to parse this down first though the toDo list. We'll call this property "HandleToggle." We're going to set that to equal this.HandleToggle.

[02:03] With that in place, I'm going to open up the toDo list component. I'm going to take props that handle toggle, and I'm going to take that down to the toDo item component.

[02:13] Define HandleToggle, this time it'll equal Props.HandleToggle. We'll open the toDoitem.js and we're going to use Props.HandleToggle as our onChange event handler. We'll define onChange and we'll set that to equal Props.HandleToggle.

[02:32] The problem here is that HandleToggle is going to receive an event object by default through this onChange. We need to do is we actually need to define an inline function here, and have that function call Props.HandleToggle parsing in the ID from the toDo, which is Props.ID.

[02:54] The other change we can make here is we can take this default.checked now that we have an onChange handler. We can change this to "checked," and it will update appropriately when onChange is called.

[03:05] On the browser, I'm going to open up the React.tools. I'm going to expand out toDos in the state. I'm going to look at the first item. We'll see here that we have our ID "1" IsComplete is set to true, our name is "Learn.jsx." That IsComplete matches what the checkbox is doing.

[03:25] If I click on that and uncheck it, we'll see that IsComplete has been updated to "false" and our IsComplete flag still matches our checkbox.

[03:34] I can check that on and off and that'll work for the second item as well because we're parsing that ID in so it toggles the appropriate toDo.

[03:46] We're defining this error function inline for this onChange handler because we need to parse some data onto our handler that's not an event object. This is something we'll need to do a lot in react components that deal with collections of data. Let's refactor this a bit to clean up the .jsx.

[04:00] Let's start by taking this entire error function, cutting it, and assigning it to a variable before a return. I'll define a variable called "HandleToggle," I'll set it to equal that error function. Down here, I'll simply call HandleToggle.

[04:18] We can take this one step further getting rid of this error function altogether. Instead using Bind to partially apply this function. I'm going to call PropsSetHandleToggle.bind. My first argument is going to be "null," because I'm not interested in resetting the context.

[04:35] My second argument will be Props.ID. This means HandleToggle is now equal to a function that already knows what its first argument's value is, which is the ID ToDo for this particular item. Having the ability to partially apply a function through bind is great, but we're going to use this in multiple places. Let's wrap this up in a utility function that cleans this up even more.

[04:57] I'm going to add a new file under Lib, create Utils.js, and I'm also going to add Utils.Test.js. I have some test code prepared. I'm going to paste that into Utils.Test.js and this file, I'm inputting partial from utils, which we haven't defined yet, but we will. I have add.Defined as a simple function that takes in two numbers and returns their sum.

[05:29] The test defines a constant called "Inc," which is a function that's the result of partially applying add with the first argument "1." We define the result which is a call to that Inc function, parsing it "2," and we expect the return result to be "3," because that first argument in this case, "A" should already be equal to "1." We set our expectation that we expect the result to be "3."

[05:53] I've saved my test file and opened up the terminals. I'm going to run the test with MPM test. Of course, the test fails because Partials doesn't exist. We're getting this type error, "Partial is not a function." Let's define it.

[06:08] In Utils.js I'm going to export a concept called "Partial." I'm going to set that to =MyFunction. When I save that, it runs, we're going to get a type error that Inc is not a function. This is not returning a function.

[06:24] First, we'll define our arguments. If you remember from the test we're parsing Add from the first argument which is our function. We're going to make this generic. We're going to use "Fn" to represent our function and we're parsing our argument.

[06:35] We want to be able to accept multiple arguments. What I am going to do is I'm going to use the Rest operator here, which is going to take a "," separated list of arguments or the rest of the arguments, anything that comes after that first one, and it's going to bundle them up in an array.

[06:53] I'm going to reference that function, I'm going to call Bind on it. I'm going to parse it "null," because I don't want to change its contacts. I need to parse those arguments into Bind. Bind takes its arguments as a "," separated list.

[07:08] We're going to use "..." again, but this time, it's the spread operator. These look the same but they're serving two different purposes. This is going to take multiple arguments turn them into an array. On the other side, we're going to spread that array back out as arguments parsed into Bind. We can save that, and our test should parse. This should apply as many arguments as we want.

[07:33] Let's update our test file, just to make sure it works with multiple arguments. I've pasted in a new test function, that's basically the same as add, but it takes a third argument and a new test that partially applies Add "3" with the arguments "1" and "3" and calls it again with "2." We expect our result to be "6."

[07:52] I'll save that. Our tests'll run and it parses. This means we can partially apply functions with as many arguments as we want and call it later with additional arguments or without and it'll run as expected.

[08:06] Let's go back to the toDo item component. We're going to import our newly created partial utility. I'll use partial and place a bind calling partial and parsing it props.HandleToggle followed by Props.ID. Back in the browser, we'll verify that everything works as expected so that we can toggle our items and the state is updated to reflect our changes.

Marko
Marko
~ 8 years ago

What is the reason using partially applied function instead of just binding arguments like it was?

Andy Van Slaars
Andy Van Slaarsinstructor
~ 8 years ago

Marko,

The point of pulling the binding out into a utility function is to make that reusable, cutting down on a bit of code in the JSX. This also opens up opportunities to partially apply arguments to any function, anywhere in your code. Say for instance you have a function that takes two arguments. You know the value of the first argument and you will get the second value as the result of a promise. You can create a partially applied version of that function with the known, first value, then in the then just pass a reference to that partially applied functions. When the promise resolves, that function will be invoked with both the initial argument and the result of the promise resolution.

Bottom line, there are a lot of places you can use partial application and when you use it a lot, it's easier to look at when you can avoid calls to bind(null, "value") all over the place.

Hope this helps.

Manuel Penaloza
Manuel Penaloza
~ 8 years ago

so to get it right... bind(null, "value") DOES NOT set this to null, but it leaves the context of the this keyword as it was when being defined in the parent component?

Andy Van Slaars
Andy Van Slaarsinstructor
~ 8 years ago

Correct:)

Gustavo Sequeira
Gustavo Sequeira
~ 8 years ago

Hi,

Using arrow function on React properties hurts performance, right? there is other way to do this? I'm looking at the onChange property of component.

Andy Van Slaars
Andy Van Slaarsinstructor
~ 8 years ago

Gustavo,

The end result of this is the use of the partial utility. That is defined as an arrow function, but could just as easily be a standard function statement or function expression. The way this is defined shouldn't have any performance implication since it is defined one time, outside of the component.

Hope this answers your question.

Gustavo Sequeira
Gustavo Sequeira
~ 8 years ago

Thanks for the quick response,

export const TodoItem = (props) => (
  <li>
    <input onChange={() => props.handleToggle(props.id)}
           type="checkbox"
           defaultChecked={props.isComplete}/>
    {props.name}
  </li>
);

So, Using () => props.handleToggle(props.id) on the onChange of TodoItem, doesn't mess with the performance? I had the idea that, using arrow functions or binding in JSX is not good for performance.

Andy Van Slaars
Andy Van Slaarsinstructor
~ 8 years ago

Gutavo,

You are correct, that code will impact performance. If you look at the end result of the lesson, you'll see that there is no arrow function in the onChange property:

https://github.com/avanslaars/egghead_react_todo_app_course/blob/lesson_13/src/components/todo/TodoItem.js#L5-L8

Gustavo Sequeira
Gustavo Sequeira
~ 8 years ago

Nice, congrats, awesome course.

Sebastián Alvarez
Sebastián Alvarez
~ 8 years ago

Andrew, first of all awesome course, I can say that I purchased PRO for this!.

Could you explain, maybe I am too newbie, why you bind to fn and dont directy call fn?

Thanks!

Andy Van Slaars
Andy Van Slaarsinstructor
~ 8 years ago

RaichuK,

Thanks, I'm so glad you enjoyed the course!

The onChange attribute needs a function as its value since that's what React will call when that event happens and we need that function to receive the item's id as an argument so we're using bind to partially apply the function with the id argument.

Our options are basically something like we started with where the onChange value is an arrow function that calls our method like onChange={() => props.handleToggle(props.id)} or we can use bind to get back the partially applied function. onChange={props.handleToggle.bind(null, props.id)}. Both of those options could get hard to read, which is why we created a partial utility function, so we have an abstraction that hides some of the ugly stuff away and we can keep the component code clean.

I hope this helps!

Oscar
Oscar
~ 7 years ago

Wow, so confused with this lesson. The Instructor goes through everything so fast :(

Michal Sedzielewski
Michal Sedzielewski
~ 7 years ago

I'm not sure that bind(null, value) doesn't override the context. A simple test proves that it happens:

const obj = { x: 1, xPlus: function(y) { return this.x + y}}
obj.xPlus(1) // -> 2
const xPlusOne = obj.xPlus.bind(null, 1)
xPlusOne() // -> NaN
Brackley Cassinga
Brackley Cassinga
~ 6 years ago

This lesson is kinda too abstract to me

Markdown supported.
Become a member to join the discussionEnroll Today