Now that we know how the forms work in React, we can move into something that helps us automate the form creation, avoid writing a lot of boilerplate, and keep our code much cleaner.
A popular solution is the react-jsonschema-form, maintained by mozilla-services, and the first thing we must do is install it using npm:
npm install --save react-jsonschema-form
Once the library is installed, we import it inside our component:
import Form from 'react-jsonschema-form'
And we define a schema as follows:
const schema = { type: 'object', properties: {
firstName: { type: 'string', default: 'Dan' }, lastName: { type: 'string', default: 'Abramov' }, },
}
We will not go into the details of the JSON Schema format in this book: the important bit here is that we can describe the fields of our forms using a configuration object instead of creating multiple HTML elements.
As you can see in the example, we set the type of the schema to object and then we declare the properties of the form, firstName and lastName, each one with a string type and its default value.
If we then pass the schema to the Form component we imported from the library, a form will be generated for us automatically.
Once more, let's start with a simple stateless functional component so that we can add features to it iteratively:
const JSONSchemaForm = () => ( <Form schema={schema} /> )
Now, if we render this component inside the page, we will see a form with the fields we declared in the schema and a submit button.
We now want to be notified when the form is submitted, to do something with the form data.
The first thing we have to do to create an event handler is transform the stateless functional component into a class:
class JSONSchemaForm extends React.Component
Inside the constructor, we bind the event handler:
constructor(props) { super(props)
this.handleSubmit = this.handleSubmit.bind(this) }
In the preceding example, we just log the form data into the console, but in a real-world application you may want to post the fields to an endpoint.
The handleSubmit handler is receiving an object, which has a formData attribute that contains the names and the values of the form fields:
handleSubmit({ formData }) { console.log(formData) }
Finally, our render method looks as follows:
render() { return (
<Form schema={schema} onSubmit={this.handleSubmit} /> )
}
Here, the schema prop is the schema object we previously defined. It can be defined
statically, as in the current example, or it can be received from the server or composed using props.
We just attach our handler to the onSubmit callback of the Form component provided by the library and we have created a working form easily.
There are also different callbacks, such as onChange, which is fired every time the value of a field changes, and onError, which is fired whenever the form is submitted using invalid data.
Events
Events work in a slightly different way across the various browsers. React tries to abstract the way events work and give developers a consistent interface to deal with. This is a great feature of React, because we can forget about the browsers we are targeting and write event handlers and functions that are vendor-agnostic.
To offer this feature, React introduces the concept of the Synthetic Event. A Synthetic Event is an object that wraps the original event object provided by the browser, and it has the same properties, no matter the browser where it is created.
To attach an event listener to a node and get the event object when the event is fired, we can use a simple convention which recalls the way events are attached to the DOM nodes. In fact, we can use the word on plus the camelCased event name (for example, onKeyDown) to define the callback to be fired when the events happen. A popular convention is to name the event handler functions after the event name and prefix them using handle (for example, handleKeyDown).
We have seen this pattern in action in the previous examples, where we were listening to the onChange event of the form fields.
Let's reiterate a basic event-listener example to see how we can organize multiple events inside the same component in a nicer way.
We are going to implement a simple button, and we start, as usual, by creating a class:
class Button extends React.Component
We add a constructor where we bind the event listener:
constructor(props) { super(props)
this.handleClick = this.handleClick.bind(this) }
We define the event handler itself:
handleClick(syntheticEvent) {
console.log(syntheticEvent instanceof MouseEvent)
console.log(syntheticEvent.nativeEvent instanceof MouseEvent) }
As you can see here, we are doing a very simple thing: we just check the type of the event object we receive from React and the type of the native event attached to it. We expect the first to return false and the second to return true.
You should never need to access the original native event, but it is good to know you can do it if you need to. Finally, in the render method, we define the button with the onClick attribute to which we attach our event listener:
render() { return (
<button onClick={this.handleClick}>Click me!</button> )
}
Now, suppose we want to attach a second handler to the button which listens to the double click event. One solution would be to create a new separate handler and attach it to the button using the onDoubleClick attribute, as follows:
<button
onClick={this.handleClick}
onDoubleClick={this.handleDoubleClick} >
Remember that we always aim to write less boilerplate and avoid duplicating the code. For that reason, a common practice is to write a single event handler for each component, which can trigger different actions according to the event type.
This technique is described in a collection of patterns by Michael Chan:
http://reactpatterns.com/#event-switch
First, we change the constructor of the component, because we now want it to bind the new generic event handler:
constructor(props) { super(props)
this.handleEvent = this.handleEvent.bind(this) }
Second, we implement the generic event handler:
handleEvent(event) { switch (event.type) { case 'click': console.log('clicked') break case 'dblclick': console.log('double clicked') break default: console.log('unhandled', event.type) } }
The generic event handler receives the event object and switches on the event type in order to fire the right action. This is particularly useful if we want to call a function on each event (for example, analytics) or if some events share the same logic.
Finally, we attach the new event listener to the onClick and onDoubleClick attributes:
render() { return ( <button onClick={this.handleEvent} onDoubleClick={this.handleEvent} > Click me!
) }
From this point on, whenever we need to create a new event handler for the same
component, instead of creating a new method and binding it, we can just add a new case to the switch.
A couple more interesting things to know about events in React are that Synthetic Events are reused, and that there is a single global handler. The first concept means that we cannot store a synthetic event and reuse it later because it becomes null right after the action. This technique is very good in terms of performance, but it can be problematic if we want to store the event inside the state of the component for some reason. To solve the problem, React gives us a persist method on the Synthetic Events, which we can call to make the event persistent so that we can store it and retrieve it later.
The second very interesting implementation detail is again about performance, and it regards the way React attaches the event handlers to the DOM.
Whenever we use the on* attributes, we are describing to React the behavior we want to achieve, but the library does not attach the actual event handler to the underlying DOM nodes.
What it does instead is attach a single event handler to the root element, which listens to all the events, thanks to the event bubbling. When an event we are interested in is fired by the browser, React calls the handler on the specific components on its behalf. This technique is called event delegation and is used for memory and speed optimization.
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 basically is to 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 React.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 mentioned previously, in general, we should try to avoid using refs because they force the code to be more imperative, and 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 which fires the instance method when clicked
Let's start by creating the input:
class Input extends React.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 = {
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 }) { this.setState({
value: target.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 React.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 components instances in case we need them to create more advanced interactions and complex structures.
Animations
When we think about UIs and the browser, we must surely think about animations as well. Animated UIs are more pleasant for users, and they are a very important tool to show users that something has happened or is about to occur.
This section does not aim to be an exhaustive guide to creating animations and beautiful UIs; the goal here is to provide you some basic information about the common solutions we can put in place to animate our React components.
For a UI library such as React, it is crucial to provide an easy way for developers to create and manage animations. React comes with an add-on, called react-addons-css-
transition-group, which is a component that helps us build animations in a declarative way. Again, being able to perform operations declaratively is incredibly powerful, and it makes the code much easier to reason about and share with the team.
Let's look at how to apply a simple fade-in effect to a text with the React add-on, and then we will perform the same operation using react-motion, a third-party library that makes creating complex animations even easier.
The first thing to do to start building an animated component is to install the add-on:
npm install --save react-addons-css-transition-group
Once we have done that, we can import the component:
import CSSTransitionGroup from 'react-addons-css-transition-group'
Then we just wrap the component to which we want to apply the animation with it:
const Transition = () => ( <CSSTransitionGroup transitionName="fade" transitionAppear transitionAppearTimeout={500} > <h1>Hello React</h1> </CSSTransitionGroup> )
As you can see, there are some props that need explanation.
First, we are declaring a transitionName. The ReactCSSTransitionGroup applies a class with the name of that property to the child element so that we can then use CSS transitions to create our animations.
With a single class, we cannot easily create a proper animation, and that is why the transition group applies multiple classes according to the state of the animation.
In this case, with the transitionAppear prop, we are telling the component that we want to animate the children when they appear on the screen.
So, what the library does is apply the fade-appear class (where fade is the value of the transitionName prop) to the component as soon as it gets rendered.
On the next tick, the class fade-appear-active is applied so that we can fire our animation from the initial state to the new one, using CSS.
We also have to set the transitionAppearTimeout property to tell React the length of the animation so it does not remove elements from the DOM before animations are completed. The CSS to make an element fade in is as follows.
First we define the opacity of the element in the initial state:
.fade-appear { opacity: 0.01; }
Then we define our transition using the second class, which starts as soon as it gets applied to the element:
.fade-appear.fade-appear-active { opacity: 1;
transition: opacity .5s ease-in; }
We are transitioning the opacity from 0.01 to 1 in 500ms using the ease-in function. That is pretty easy, but we can create more complex animations, and we can also animate different states of the component.
For example, the *-enter and *-enter-active classes are applied when a new element is added as a child of the transition group.
A similar thing applies to removing elements.