Refs

One of the reasons people love React is because it is declarative. Being declarative means that you just describe what you want to be displayed on the screen at any given point in time and React takes care of the communications with the browser. This feature makes React very easy to reason about and it is very powerful at the same time.

However, there might be some cases where you need to access the underlying DOM nodes to perform some imperative operations. It should be avoided because, in most cases, there is a more React-compliant solution to achieve the same result, but it is important to know that we have the possibility to do it and to know how it works so that we can make the right decision.

Suppose we want to create a simple form with an input element and a button, and we want it to behave in such a way that when the button is clicked, the input field gets focused.

What we want to do, is call the focus method on the input node, the actual DOM instance of the input, inside the browser's window.

Let's create a class called Focus with a constructor where we bind the handleClick method:

  class Focus extends Component

We will listen to the click events on the button to focus the input field:

  constructor(props) { 
super(props);

this.handleClick = this.handleClick.bind(this);
}

Then, we implement the actual handleClick method:

  handleClick() { 
this.element.focus();
}

As you can see, we are referencing the element attribute of the class and calling the focus method on it.

To understand where it comes from, you just have to check the implementation of the render method:

  render() { 
return (
<form>
<input
type="text"
ref={element => (this.element = element)}
/>
<button onClick={this.handleClick}>Focus</button>
</form>
);
}

Here comes the core of the logic. We create a form with an input element inside it and we define a function on its ref attribute.

The callback we defined is called when the component gets mounted, and the element parameter represents the DOM instance of the input. It is important to know that, when the component gets unmounted, the same callback is called with a null parameter to free the memory. What we are doing in the callback is storing the reference of the element to be able to use it in the future (for example, when the handleClick method is fired). Then, we have the button with its event handler. Running the preceding code in a browser will show the form with the field and the button, and clicking on the button will focus the input field, as expected.

As we mentioned previously, in general, we should try to avoid using refs because they force the code to be more imperative, and they become harder to read and maintain.

The scenarios where we might need to use it without any other solutions are the ones where we are integrating our components with other imperative libraries, such as jQuery. It is important to know that when setting the ref callback on a non-native component (a custom component that starts with an uppercase letter), the reference we receive as a parameter of the callback is not a DOM node instance, but the instance of the component itself. This is really powerful because it gives us access to the internal instance of a child component, but it is also dangerous and should be avoided. To see an example of this solution, we are going to create two components:

  • The first one is a simple controlled input field, which exposes a reset function that resets the value of the input itself to an empty string
  • The second component is a form with the previous input field and a reset button that fires the instance method when clicked

Let's start by creating the input:

  class Input extends Component

We define a constructor with a default state (empty string) and bind the onChange method we need to control the component, and the reset method, which represents the public API of the component:

  constructor(props) { 
super(props);

this.state = {
value: ''
};

this.reset = this.reset.bind(this);
this.handleChange = this.handleChange.bind(this);
}

The reset function is very simple, and just brings the state back to empty:

  reset() { 
this.setState({
value: ''
});
}

The handleChange is pretty simple as well, and it just keeps the state of the component in sync with the current value of the input element:

  handleChange({ target: { value } }) { 
this.setState({
value
});
}

Finally, inside the render method, we define our input field with its controlled value and the event handler:

  render() { 
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}

Now, we are going to create the Reset component, which uses the preceding component, and call its reset method when the button is clicked:

  class Reset extends Component

Inside the constructor, we bind the event handler, as usual:

  constructor(props) { 
super(props);

this.handleClick = this.handleClick.bind(this);
}

The interesting part is the code inside the handleClick function because this is where we can call the reset method on the instance of the input:

  handleClick() { 
this.element.reset();
}

Finally, we define our render method, as follows:

  render() { 
return (
<form>
<Input ref={element => (this.element = element)} />
<button onClick={this.handleClick}>Reset</button>
</form>
);
}

As you can see here, referencing node elements or instances is basically the same in terms of ref callback.

This is pretty powerful because we can easily access methods on the components, but we should be careful because it breaks the encapsulation and makes refactoring pretty hard. Suppose, in fact, that you need to rename the reset function for some reason; you have to check all the parent components that are using it and change them as well.

React is great because it gives us a declarative API to use in all cases, but we can also access the underlying DOM nodes and component instances in case we need them to create more advanced interactions and complex structures.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.67.251