Understanding Forms in React

Understanding Forms in React

Forms are an integral part of every application be it web or mobile and they are a perfect way of collecting information in our applications. In React, working with forms can sometimes be overwhelming and a bit complex most especially if you’re new to the React ecosystem.

This post is targeted at those people who still don’t understand how React deals with forms.

According to the React official documentation, there are two types of forms in React, there are Uncontrolled and Controlled forms. Let’s look at them one after the other with some code examples.

Uncontrolled Forms

Uncontrolled forms are forms that leave their values to be managed by the DOM. In other words, you can say that they don’t have their state stored on the component they are declared on. Let's take a look at an example to understand this properly.

const Uncontrolled = () => {
    render (
        <form>
            <input type=“text” />
            <button type=“submit”>Submit</button>
        </form>
    );
}

If we try running this snippet of code on the browser we will notice that everything works as expected; that means when will type in a value in the form field the form responds by showing the values on the form field.

But what if we want to collect and use the value from the input field we have declared? Well, in React there is a concept called Events Handler. Event handlers are callback functions in a React component that handles certain operations when an event is triggered. In our case, we want to listen to the onChange event, so let’s find out how we can achieve this.

const Uncontrolled = () => {
    const handleChange = ({ target: { value } }) => {
        console.log(value);
    }

    render (
        <form>
            <input type=“text” onChange={handleOnChange} />
            <button type=“submit”>Submit</button>
        </form>
    );
}

This code snippet works perfectly well in the way we expect it to. If you try typing in 'React' into the input field you will notice the following changes on the console:

R
Re
Rea
Reac
React

What is happening here is that the input field is listening for an onChange event which also calls the handleChange event handler we declared in our component. The handleChange event handler takes the event object as parameter and then destructure out the current value of the input field each time the value changes.

This use case is a better fit when you are dealing with a form that has just one input field and a perfect example is a subscribe form that has an input field to collect the user’s email. But what if we want to handle multiple input fields with different types?

Now let’s take a look at how to solve this problem using controlled forms.

Controlled Forms

Controlled forms are a type of forms that have their state/values managed by the component (React). It is perfect when we want to prefill the form with some values, which we may receive from the server or as props from the parent.

To understand this concept, we will start again by creating a simple stateless component, and improve on it step by step.

const Controlled = () => {
    render (
        <form>
            <input type=“text” value=“Hello World” />
            <button type=“submit”>Submit</button>
        </form>
    );
}

In the code snippet above, we have declared a simple stateless functional component that has an input text field with a default value “Hello World”. If we run this component on the browser, we realize that it is showing the default value but when you try to change the value, it doesn’t change.

The reason it does that is that, in React, we declare what we want to see on the screen, and setting a fixed value attribute always ends up rendering that value, no matter what other actions are taken. This is unlikely to be the behavior we want in a real-life use case.

If we open the console, we will notice an error message, React itself is telling us that we are doing something wrong.

You provide a ‘value’ prop to a form field without an ‘onChange’ handler. This will render a read-only field.

And this is exactly what is happening.

Now, if you want to have a default value on the form field without breaking React’s rule, you can use the defaultValue attribute provided by React to declare the default value for the input field.

const Controlled = () => {
    render (
        <form>
            <input type=“text” defaultValue=“Hello World” />
            <button type=“submit”>Submit</button>
        </form>
    );
}

In this way, we are free to type in the input field and change its value. But then, our component is still missing an important functionality, right now, we can't get the value of the input field after we are done typing. Now let’s improve on our form and see how we can properly make use of controlled forms in React.

class Controlled extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            firstName: “John”,
            lastName: “Doe”
        }
    }

    handleChange = ({ target: {name, value}}) => {
        this.setState({
            [name]: value,
        });
    }

    handleSubmit = (event) => {
        event.preventDefault();
        console.log(`${this.state.firstName} ${this.state.lastName}`);
    }

    render (
        <form onSubmit={this.handleSubmit}>
            <input 
                name=“firstName” 
                type=“text” 
                value={this.state.firstName} 
                onChange={handleChange}
            />
            <input
                name=“lastName”
                type=“text”
                value={this.state.lastName}
                onChange={handleChange}
            />
            <button type=“submit”>Submit</button>
        </form>
    )
}

The important thing to notice in our new component is that it is a stateful class component, and the first time the form renders, React uses the initial values from the state as the default value of the input fields. When the user types something into the field, the handleChange handler/function is called and the new value for the field is stored in the state.

When the state changes, React re-renders the component and uses it again to reflect the current value of the input fields.

We now have full control over the value of the fields, and we call this pattern controlled components.

Conclusion

So far in this post, we have seen how to use Uncontrolled and Controlled forms in React and their different use cases, but please put in mind that forms are always a time-consuming part of every application most especially as developers. If you want to handle things like validation and other advanced stuff when dealing with forms in React, I strongly recommend these powerful libraries Formik and React-hook-form that is if you are writing your component with React Hooks.

I hope you enjoyed this post, and thank you for reading.