Skip to content

Instantly share code, notes, and snippets.

@ellereeeee
Created January 16, 2018 08:24
Show Gist options
  • Save ellereeeee/9ca0050f71bb36402336488dc57b6f09 to your computer and use it in GitHub Desktop.
Save ellereeeee/9ca0050f71bb36402336488dc57b6f09 to your computer and use it in GitHub Desktop.

![React logo](pictures/React logo.png)

This is the sixth part of my notes on egghead.io's The Beginner's Guide to ReactJS.

Use Class Components with React

This section will show how implicit this bindings are lost when updating state in React and how to deal with it in different ways. The title of the video would be better named "Setting State and this".

Implicit Binding and Losing It

The this binding is determined by these rules in order of precedence:

  1. If new is called, this is bound to the newly constructed object.

  2. If call or apply (or bind) is called, this is bound to the specified object.

  3. If a context object owning the call is called, this is bound to the context object.

  4. this by default is undefined in strict mode or a global object otherwise.

We will focus on number 3, which is called implicit binding.

This is a vanilla JavaScript example of an implicit binding and the implicit binding being lost.

https://gist.github.com/18db5581c12ed804bff703cbda103607

Although bob doesn't "own" the function greet, you could say it does at the time bob.greet is called. In this situation, we say bob is the context object and this is "implicitly" bound to bob.

The this binding to bob is lost when bob.greet is assigned to the plain function greetFn because greetFn is a plain function that references greet. The default binding is applied and GLOBAL OBJECT NAME is logged to the console.

It's important to note that even though it greetFn looks like a reference to bob.greet, it's actually just a reference to the function greet.

Let's look at something similar:

https://gist.github.com/b882f366b298806fb168c29849375794

Since there is no global variable "name," nothing is returned.

Now we can understand how something similar happens when updating state in React, and how to deal with it.

Losing this Binding in Class Components

We'll start by making a button that increments a number when it's clicked.

https://gist.github.com/a0aa095c00eca13a0d4aed18a88c3a0a

![The initial incrementing button. It works.](gifs/initial counter.gif)

Focus on on the onClick value. We have an arrow function that increments state with this.setState.

While this code is completely functional, we could assign the this.setState function to an event handler for more readability.

The edited code would look like this:

https://gist.github.com/5e94369f92639f3f52197db83bc16c06

![The button does not work](gifs/broken counter.gif)

Uh oh! It doesn't work anymore. If we open the developer's console, we get Uncaught TypeError: Cannot read property 'setState' of undefined.

this's binding in this.setState to the object created by class Counter was lost when we assigned it to handleClick(). We are just assigning a reference to the setState function to handleClick(). handleClick() is also just a plain function. Because classes in ES6 are always run in strict mode, this defaults to undefined. Notice the parallels from the example above in JavaScript.

Preserving this Binding in Class Components

We'll cover several ways to ensure the this binding is not lost.

One way uses "explicit binding," or rule number 2 from the above this binding rules. This is when we explicitly say what we want this to bind to with call, apply, or bind.

Here we'll use the built-in bind utility in the onClick assignment. bind returns a new function that is hard-coded to call the original function with the this context object you specified. This variation of explicit binding involving assigning call, apply, or bind expressions to functions is called "hard binding."

The timer will function properly if we edit onClick like this:

onClick={this.handleClick.bind(this)}

While this works, this could be a performance bottleneck in some situations.

To deal with the bottleneck we can reference the prototypal method handleClick() by adding this.handleClick in the constructor and assiging it a pre-bound handleClick method.

The contructor would look like this:

https://gist.github.com/4ac318b3f926d62295d81b844b0b80d6

This is the same as the common pattern of lexically capturing this in pre-ES6 code, like in var self = this;.

This solution can be cumbersome if we need to do this for a lot of methods.

Let's look at another solution with public class fields. In a nutshell, these let us declare class properties without the constructor.

Look at the code below for before and after public class fields are implemented.

Before:

https://gist.github.com/86ee67a95c097a7b2b775dc27900dd1a

After:

https://gist.github.com/c2a27c462691ea4b0b0559389f184c63

We've moved this.state and the this.handeClick assignments out of the constructor and into the class body. this. is no longer necessary since state and handleClick are in the class body. state and handeClick are the public class fields and we assign expressions to them. Since the constructor has become the same as the default constructor, we've removed it.

The incrementing counter will work with this implementation.

We could refactor this by removing the handleClick() method from the prototype and having it only on the instance.

https://gist.github.com/0e167ba3b9793f5627d03e55ddb55a2e

While this works, we have to call the .bind method everytime we update state. You can use the lexical this that are possible with arrow functions to workaround this.

https://gist.github.com/a36c37bc5da1af09fa83c74671e7c2ab

Which way is best?

We discussed four main ways:

  1. Hard binding the method with .bind in event handler assignment value

  2. Lexically capturing the prototype method in the constructor

  3. A variation of number 2 using public class fields

  4. Using lexical this via arrow functions

The issue with number 1 is that it can be a performance bottleneck in some situations. Number 2 can be cumbersome if we have to do it for many methods. Number 3 is currently an experimental implementation; it's probably not good for production code (although it's commonly used in many projects).

Number 4 works and is very common, but it's not without its criticisms. YDKJS author Kyle Simpson says that using lexical this reinforces a bad practice of evading a solid understanding of this. He suggests people either stick with lexical style code or embrace this and avoid lexical this.

Either way it's important to understand both hard binding and lexical this so you can understand others' code and adapt to any situation when working in a team.

TL;DR

Be careful of losing the this binding when updating state in class components. You can keep the binding with either the .bind method, capturing lexical this via assignment in the constructor or with public class fields, or with the lexical this included in arrow functions.

Manipulate the DOM with React refs

This section will explain how to manipulate the DOM node directly with React's ref prop. Sometimes this is necessary for some JavaScript libraries to work or when we want to get the value of form fields.

We'll use vanilla-tilt.js as an example on how to make a JavaScript library functional with ref.

Making a Static Image

This is our base code:

https://gist.github.com/4268e70d9d0000b2e4f562cf4ad71464

Class Tilt renders a div with the class tilt-root, which nests a div with the class tilt-child, which nests a div that spreads the props. tilt-root styles one div to have the colored gradient while tilt-child is a smaller white box. Both also have styles for animations we'll see later.

We then create constant element, which is the Tilt component wrapped in a div with the class totally-centered. There is another div with the same class nested inside of Tilt with the words "vanilla-tilt.js". totally-centered are flexbox styles that horizontally and vertically center content.

This code produces this static image:

![Static white box in a colored gradient box.](pictures/static vanilla-tilt.png).

Adding ref

Taken from the React docs,

The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.

When the ref attribute is used on an HTML element, the ref callback receives the underlying DOM element as its argument.

In our code, we'll use the ref callback to store a reference to our desired DOM node.

The render method now returns this:

https://gist.github.com/4c2aae3060fdf41105bd42b59fac5ee6

We can see we're accessing the node we want with:

https://gist.github.com/562671b87ba5bd981dd21c87a32ab58a

VanillaTilt working.

It works!

TL;DR

To manipulate the DOM,

  1. Pass on a ref on the element that you're rendering. Putting ref on a class references an instance of that class.

  2. In the ref value, pass in the node as the argument and return an assignment of the node to a value in the instance (this.something).

  3. After the component is mounted, the node can be used for a library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment