Hooks are stable as of React v16.8! The Hooks Proposal is an attempt to address several major concerns developers have with React. Essentially, a Hook is a special function that allows you to “hook into” React features. Hooks are ideal if you’ve previously written a functional component and realized that you need to add state to it.
If you’re new to Hooks and would like an overview, check out our introduction to React Hooks.
useState
declares a state variable to preserve values between function calls. The variables are preserved by React. useState
only takes one argument that initializes the value of the state that you’re setting. By implementing useState
we no longer need this.example
, we can access the variable directly.
In this tutorial, you will build upon a previously written class-based component and convert it into a functional component using the useState
Hook.
This tutorial was verified with Node v16.4.0, npm
v7.20.3, react
v17.0.2, react-dom
v17.0.2, react-scripts
v4.0.3, bootstrap
v4.6.0, and reactstrap
v8.9.0.
This tutorial builds upon some starter code. In the starter code, we installed the latest version of react
and react-dom
as well as reactstrap to help us have some easy formatting.
The component consists of a form with fields for email
and password
. When the form is submitted, the values are logged to the console.
First, open your terminal and navigate to your working directory. Then clone the repo:
- git clone https://github.com/do-community/convert-class-to-hook
Next, navigate to the new project directory:
- cd convert-class-to-hook
Then, install the package dependencies:
- npm install
Take a moment to familiarize yourself with the current ClassBasedForm.js
component located in the components
directory.
import React from 'react'
import {
Form, FormGroup, Input,
Label, Col, Button,
} from 'reactstrap'
export default class ClassBasedForm extends React.Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
console.log(this.state);
}
render() {
return (
<Form onSubmit={ this.handleSubmit }>
<h1>Class-Based Form</h1>
<FormGroup row>
<Label for="exampleEmail" sm={ 2 }>Email</Label>
<Col sm={ 8 }>
<Input
type="email"
name="email"
id="exampleEmail"
placeholder="email"
value={ this.state.email }
onChange={ (event) => this.setState({ email: event.target.value }) }
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="examplePassword" sm={ 2 }>Password</Label>
<Col sm={ 8 }>
<Input
type="password"
name="password"
id="examplePassword"
placeholder="password"
value={ this.state.password }
onChange={ (event) => this.setState({ password: event.target.value })}
/>
</Col>
</FormGroup>
<FormGroup check row>
<Col sm={ { size: 'auto', offset: 8 } }>
<Button>Submit</Button>
</Col>
</FormGroup>
</Form>
)
}
};
Finally, verify your installation by running the application:
- npm start
Open the application in the browser. Enter values for the email and password and submit the form.
At this point, you have a working class-based component. None of this behavior should change when converting the class-based component to a functional component.
In this section, you will create an additional Form
component in the components
folder. This will be a FunctionBasedForm.js
file. We’ll use this component to build an identical form using the useState
Hook.
Having the class-based component and new functional component side-by-side will allow you to compare the two implementations.
We’ll first import React and create a function variable called FunctionBasedForm
that returns some text. Be sure to export this function.
import React from 'react';
const FunctionBasedForm = () => {
return (
<h1>Function Based Form</h1>
)
};
export default FunctionBasedForm;
Compare this to the class ClassBasedForm extends React.Component { ... }
declaration used in ClassBasedForm.js
.
Next, you will add your new component to App.js
. Open App.js
in your code editor.
For now, comment out the previous ClassBasedForm
import.
Import the FunctionBasedForm
component. Replace your previous component with your new <FunctionBasedForm />
component in the return
statement.
import React, { Component } from 'react';
import { Container } from 'reactstrap';
// import ClassBasedForm from './components/ClassBasedForm';
import FunctionBasedForm from './components/FunctionBasedForm';
import Logo from './assets/alligator-logo2.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<img src={ Logo } alt="alligator.io logo" width="200" />
<Container>
<FunctionBasedForm />
</Container>
</div>
);
}
}
export default App;
Save your changes and run the application. Then visit localhost:3000
in the browser:
The application will display the text from the new component.
email
and password
In our original ClassBasedForm.js
component we initialize our state using a constructor. We no longer need to initialize state in a typical class component, nor do we for hooks! Let’s consider the following example.
Most of us are familiar with initializing state like so:
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
Since React 16, we no longer need a constructor. The former becomes:
state = {
email: '',
password: '',
};
Now in a functional component, we can use hooks. We can initialize state and the related setter at the beginning of our function. This could potentially eliminate several lines of code for us. Here’s how we would initialize state variables in our new component.
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
Let’s break down one of the lines above.
const [ email, setEmail] = useState('');
const
: Creates a variable for both our state and the associated state variable setter.email
: Initializes and declares our first variable email
.setEmail
: Initializes the setter function associated with the variable email
. Though it may seem redundant useState
is only intended to be used for a single value.useState('')
: Declares that the variable email
starts as an empty string.Now, revisit FunctionBasedForm.js
in your code editor.
Copy code from ClassBasedForm.js
and remove the functions, click handlers, and state variables.
Next, add { useState }
to your React import. Also, add the state variables defined in the previous section.
import React, { useState } from 'react';
import {
Form, FormGroup, Input,
Label, Col, Button,
} from 'reactstrap'
const FunctionBasedForm = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<Form>
<h1>Function Based Form</h1>
<FormGroup row>
<Label for="exampleEmail" sm={ 2 }>Email</Label>
<Col sm={ 8 }>
<Input
type="email"
name="email"
id="exampleEmail"
placeholder="email"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="examplePassword" sm={ 2 }>Password</Label>
<Col sm={ 8 }>
<Input
type="password"
name="password"
id="examplePassword"
placeholder="password"
/>
</Col>
</FormGroup>
<FormGroup check row>
<Col sm={ { size: 'auto', offset: 8 } }>
<Button>Submit</Button>
</Col>
</FormGroup>
</Form>
)
};
export default FunctionBasedForm;
Save your changes and visit the application in your browser.
At this point, the functional component is visually identical to the class-based component.
onChange
and handleSubmit
Now let’s revise our functions to utilize Hooks.
Let’s revisit how we updated state in our class-based component:
onChange={ (event) => this.setState({ email: event.target.value })
With hooks, we no longer need this
or this.setState()
since we’re already initiating our state variables and attaching a setter.
Since there are two variables, we’re going to use an inline function to call the setter that we initiated in useState
for each input. We’ll also add our value back without the this
prefix.
For the email field:
<Input
type="email"
name="email"
id="exampleEmail"
placeholder="email"
value={ email }
onChange={ event => setEmail(event.target.value) }
/>
For the password field:
<Input
type="password"
name="password"
id="examplePassword"
placeholder="password"
value={ password }
onChange={ event => setPassword(event.target.value) }
/>
Now let’s rewrite our handleSubmit
function.
Here’s how the function was previously written:
handleSubmit(e) {
e.preventDefault();
console.log(this.state);
}
Note: In React 16, if you write this as an arrow function, you won’t need to bind it in the constructor.
We now need to create a const
for the function. We again prevent the default functionality, set the variables, and console.log
them.
const handleSubmit = e => {
e.preventDefault();
console.log(email);
console.log(password);
}
Now we can add our handleSubmit
function to the onSubmit
in our form.
Here’s how your new functional hook should look:
import React, { useState } from 'react'
import {
Form, FormGroup, Input,
Label, Col, Button,
} from 'reactstrap'
const FunctionBasedForm = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = event => {
event.preventDefault();
console.log(email);
console.log(password);
}
return (
<Form onSubmit={ handleSubmit }>
<h1>Function Based Form</h1>
<FormGroup row>
<Label for="exampleEmail" sm={ 2 }>Email</Label>
<Col sm={ 8 }>
<Input
type="email"
name="email"
id="exampleEmail"
placeholder="email"
value={ email }
onChange={ event => setEmail(event.target.value) }
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="examplePassword" sm={ 2 }>Password</Label>
<Col sm={ 8 }>
<Input
type="password"
name="password"
id="examplePassword"
placeholder="password"
value={ password }
onChange={ event => setPassword(event.target.value) }
/>
</Col>
</FormGroup>
<FormGroup check row>
<Col sm={ { size: 'auto', offset: 8 } }>
<Button>Submit</Button>
</Col>
</FormGroup>
</Form>
)
};
export default FunctionBasedForm;
Save your changes and visit the application in your browser.
Add some values to your form and use the Submit button. Open the developer tools and display the console messages. Values for the email
and password
will display.
In this tutorial, you built upon a previously written class-based component and converted it into a functional component using the useState
Hook.
Continue your learning with How To Apply React Hooks in a React Project.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!