React Daily UI - 001 Sign Up Form

Edit this page on Github

This post is a part of the React Daily UI post series, a joint effort between Jack Oliver, Sophia Shoemaker, and the rest of the team at Fullstack React. Each day we're explaining in detail how to create a UI component with React.

You can view the Codepen implementation here

Or you view the code on Github here

Welcome to React Daily UI, where we go 100 days building 100 beautiful React applications. We're really excited to be partnering with Jack Oliver who is embarking on this ambitious project with us.

Jack is designing and writing the code for these applications and we're going to deconstruct each one to highlight the features that are unique to React.

Kicking it off today, we're going to make a sign up form. Here's a live demo:

Overview

This sign up form has a few different features that are unique to React. The sign up form itself is a modal that uses CSS animations and slides down when the page loads and slides up after the user logs in. We are going to learn about the following features for this sign up form:

  • Using state and props
  • CSS animations
  • Event handling
  • Native browser validation for forms

Table of Contents

Mounting with state

React uses the concept of components which, conceptually are containers for our data and UI. Each component has several properties and methods that we will take advantage of in this post and for the rest of the series.

Additionally, a React component can define it's own state using a state property for handling stateful components, such as our form element today.

Using the state property allows us to manipulate a React component's view and data associated with the view to keep track of the local state of the component.

What is state?

When we refer to a component's state, we mean a snapshot of the instance of the component on the page.

In regular HTML (without React), when we have a text <input /> box on a page, the state of the <input /> element is that the value of the <input /> component is a blank string (i.e. ""). When our user types into the <input /> box, the state changes for the <input /> box to set the value to the keystroke the user made.

React's components can define their own state which we'll use in today's post, and others in the future. When we use state in a React component the component is said to be stateful.

In this particular instance we are going to use the component's state to show and hide the form on the page.

Let's create the sign up form by first wrapping it in a parent App component. This way we can define some methods in this parent component that we can use to show or hide the sign up modal view.

The basic stateful App component looks like this:


var App = React.createClass({
    
    getInitialState: function() {
        return { mounted: false };
    },
    
    componentDidMount: function() {
        this.setState({ mounted: true });
    },
    
    handleSubmit: function(e) {
        this.setState({ mounted: false });
        e.preventDefault();
    },

    render: function() {
        var child;

        if(this.state.mounted) {
            child = (<Modal onSubmit={this.handleSubmit} />);
        }
        
        return(
            <div className="App">
                <ReactCSSTransitionGroup 
                    transitionName="example"
                    transitionEnterTimeout={500}
                    transitionLeaveTimeout={300}>
                        {child}
                </ReactCSSTransitionGroup>
            </div>
        );
    }
});

React components have a method that is frequently used called getInitialState.

React expects us to return a JavaScript object from this method that stores any sort of data we want to manipulate or display in the component.

Let's tell React that the App component keeps a single item in it's local state, a boolean we will call mounted.


getInitialState: function() {
    return { mounted: false };
},

React components also have 'lifecycle hooks' where we can define custom functionality during the different phases of the component. These methods are executed at specific points in a component's lifecycle 1.

One of these hooks is the componentDidMount method is executed just after the component has been rendered to the page. In order to define functionality during the lifecycle, we need to define the method in the component:


componentDidMount: function() {
    this.setState({ mounted: true });
},

This method is where we set our mounted state to true as now our form is inserted into the DOM and our component is prepared to show it. We change the state of our data using a component method setState


this.setState({ mounted: true });

Although a component's state is available via this.state, we should treat this.stateas a readonly object and only ever change the state using the setState method available on a React component.

The setState method sends the state object into a queue to be batched for DOM updates, so modifying or changing any portion of a component's state should only happen via setState.

We only want to show the modal if it is mounted, so we can include a conditional rendering statement in our component that renders the form if and only if the component has been mounted.



        if(this.state.mounted) {
            child = (<Modal onSubmit={this.handleSubmit} />);
        }
        

This conditional statement creates the child component if the App component has been rendered to the DOM. Our child component, Modal contains a form inside of it.

The Form

Our Modal component contains a form that looks familiar if you've built a form with HTML before. However, there are a few differences between this form and a typical HTML form. The form contains a props attribute as well as a custom <Input/> component that also uses this.props.

this.props

In addition to the state property, React components have another property called props.

The difference between state and props can be a little confusing at first. props are used to pass down data and event handlers from parent components to child components. state on the other hand is used to manipulate the current state of a component.

Notice that a child component does not pass any data back up to it's parent component. This means we can only ever pass data down the component tree. This pattern of data passing is called one-way databinding.

A child component can inform the parent component about an update it makes, but cannot change the data itself. We'll look at this process next:

Another aspect about the props attribute that can be a little confusing is passing down a function as a prop. When our Modal component is created, we pass down the App component's handleSubmit function as a prop:


child = (<Modal onSubmit={this.handleSubmit} />);

Then, in our form itself, the onSubmit event handler is given the submit callback function we passed down as a prop:


<form 
    onSubmit={this.props.onSubmit}
    className="ModalForm">

The function we define as onSubmit can then be called by the child <Modal /> component when the user hits the submit button in the view. The actual code for the function is found in the parent component:


handleSubmit: function(e) {
    this.setState({ mounted: false });
    e.preventDefault();
},

When the user clicks the submit button this event handler is called and the mounted variable gets set back to false.

We also have three Input components which we'll define next.

Building reusable inputs

We can define a reusable component, which can take in props that get passed down from the parent <form /> component. Three props we'll want to pass into the <Input /> component are:

  • name
  • type
  • placeholder

These particular values are JavaScript strings, unlike the onSubmit event handler where we passed a function.

These props allow us to create reuseable components since we just have to pass in the type of input (either text, email or password), the name we want to associate with the input element, and the placeholder to a normal input element.


var Input = React.createClass({
    render: function() {
        return (
            <div className="Input">
                <input 
                    id={this.props.name}
                    autoComplete="false"
                    required
                    type={this.props.type}
                    placeholder={this.props.placeholder}
                />  
                <label htmlFor={this.props.name}></label>
            </div>
        );
    }
});

Native browser validation

Two of the input types we are using with this form are slightly different than the normal text type: password and email. With these input types, the browser will automatically do some validation and render the inputs slightly differently than a normal text type. Using the email type will make the browser do some simple validation of the text, including checking for the @ symbol. Using the password type automatically masks the input.

Animating with ReactCSSTransitionGroup

Animations in React are not always as simple as just adding a few transitions and translations to our CSS files. Since React's algorithm works differently than other JavaScript libraries such as JQuery, React requires some DOM nodes to be removed/added to the DOM instead of manipulated.

Adding animations via CSS files aren't always straight-forward. To address this problem, React provides an addon library to ease with the difficulties: ReactCSSTransitionGroup.

The ReactCSSTransitionGroup library is not included by default when using the create-react-app tool. Although this library is included with the code for this post, to use it in your own projects, we'll need to install it using the npm package manager:

npm install react-addons-css-transition-group

Let's look at how to use ReactCSSTransitionGroup with our form. We'll use a few props on the ReactCSSTransitionGroup element which define the CSS transition name as well as a few props to define variables:


    render: function() {
        var child;

        if(this.state.mounted) {
            child = (<Modal onSubmit={this.handleSubmit} />);
        }
        
        return(
            <div className="App">
                <ReactCSSTransitionGroup 
                    transitionName="example"
                    transitionEnterTimeout={500}
                    transitionLeaveTimeout={300}>
                        {child}
                </ReactCSSTransitionGroup>
            </div>
        );
    }

When the App component is mounted, our child variable is assigned our Modal component:


        var child;

        if(this.state.mounted) {
            child = (<Modal onSubmit={this.handleSubmit} />);
        }

By wrapping the modal in a ReactCSSTransitionGroup component, we get access to the animation effect whenever the modal is added and removed from the DOM.

The ReactCSSTransitionGroup must be available and mounted in the DOM for the animations to take effect.

If the ReactCSSTransitionGroup node is mounted at the same time as it's children, the animations will not work. The ReactCSSTransitionGroup has two properties that manage the durations of the animation when the modal enters/leaves the DOM:


<ReactCSSTransitionGroup 
    transitionName="example"
    transitionEnterTimeout={500}
    transitionLeaveTimeout={300}>

Using ReactCSSTransitionGroup is great because our component code is declarative and the CSS manages the transitions in between our states of mounted and unmounted (e.g. we're not iterating over opacity and y position).

Try it out!

Check out the Codepen example:

The complete source for this article is also available on Github here.

To start the app, download the code, cd into the project directory and type:

   npm install
   npm start

Sophia Shoemaker

Sophia Shoemaker became addicted to React in 2014. She is a full stack developer but React is her technology of choice. She loves working on her pet project Shop Sifter and editing the Fullstack React Newsletter.

Recently, she became a mentor at Hackbright Academy and is excited to encourage and empower women looking to get involved in programming. When she isn't cobbling code, she can be found flying in her husband's Beechcraft Bonanza and playing with her 2 kids.

Connect with Sophia on Twitter at @wisecobbler.

Jack Oliver

Hi, I'm Jack! I'm a Developer & Designer living in Stockholm, Sweden. I've worked with super cool people; from Mercedes-Benz, Farfetch, NotOnTheHighStreet, and Mimecast, and am currently building cool stuff at Observify. Part-time photographer, full-time joker. I'm currently doing 100 days of React on Codepen check it out here.

Connect with me on twitter @mrjackolai.