React converting to a hook with useState and useEffect App Demo
310
Hooks are all the rage in React, especially now that they 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.
In this tutorial, we’re going to take a previously written class-based component and convert it into a functional component using the useState Hook. useState Overview
Simply put, 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.whatever, we can access the variable directly.
To help speed things along, we’ve prepared 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.
To get started:
$ docker run -d -p 3001:3000 mltdocker/react-converting-to-a-hook_app
switching component/ClassBasedForm, ClassBasedForm1, ClassBasedComponent to display diferrent output on Browser.
$ sudo docker exec -it <<<docker_container_id>>> bash
Now let’s revise our functions to utilize Hooks.
Let’s take a look at 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 we only have two variables we’re using, 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.
<Input
type="email"
name="email"
id="exampleEmail"
placeholder="email"
value={ email }
onChange={ event => setEmail(event.target.value) }
/>
<Input
type="password"
name="password"
id="examplePassword"
placeholder="password"
value={ password }
onChange={ event => setPassword(event.target.value) }
/>
If we had several variables and wanted to share them between components, we would use an additional hook that we’ll cover in a later article.
Now let’s rewrite our handleSubmit function.
Here’s how the function was previously written:
handleSubmit(e) {
e.preventDefault();
console.log(this.state);
}
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: 10, offset: 8 } }>
<Button>Submit</Button>
</Col>
</FormGroup>
</Form>
)
};
export default FunctionBasedForm;
Flip back over to your browser and add some values to your form and hit submit. If your app console.log()s came back with the variables you entered, you’re golden! Well done!
React Hooks are revolutionizing the way we develop in React and solving some of our biggest concerns. The useEffect Hook allows us to replace repetitive component lifecycle code.
Essentially, a Hook is a special function that allows you to “hook into” React features. Hooks are a great solution if you’ve previously written a functional component and realize that you need to add state to it.
If you’re new to Hooks and would like an overview, check out the introduction to React Hooks.
This article assumes that you’re familiar with the useState Hook. If you’re not, never fear! If you spend a little time with Convert a React Class-Based Component to a Functional One Using a State Hook you’ll be on the right track!
useEffect is short for ‘use side effect’. Effects are when our application reacts with the outside world, like working with an API. It allows us to run a function based on whether something changed. useEffect also allows us to combine componentDidMount and componentDidUpdate.
We’ll be taking some prewritten class-based code and converting it to a functional component. We’ll be using reactstrap to simplify our formatting and axios to call an external dummy API.
Specifically, we’re using jsonplaceholder to pull-in dummy user data on our initial component mount.
Take a moment to familiarize yourself with the code, in particular, the ClassBasedComponent.js file.
You’ll notice that we have two lifecycle methods in this file, componentDidMount and componentDidUpdate.
async componentDidMount() {
const response = await axios
.get(`https://jsonplaceholder.typicode.com/users`);
this.setState({ users: response.data });
};
async componentDidUpdate(prevProps) {
if (prevProps.resource !== this.props.resource) {
const response = await axios
.get(`https://jsonplaceholder.typicode.com/users`);
this.setState({ users: response.data });
}
};
These are both async lifecycle methods that call the jsonplaceholder API to bring in a list of users.
In componentDidMount, we say on first render, get the user data. Next, on componentDidUpdate we look to see if anything has changed in props. This can be triggered from use
We would like to condense the lifecycle methods into the useEffect Hook and create a function-based component.
Rather than using the same ClassBasedComponent.js file, create a new file called FunctionBasedComponent.js. We’re creating a new file so that we can contrast and compare the two.
In your terminal, you can run the following to create the new file from your root directory:
$ touch FunctionBasedComponent.js
To help get started, copy and paste the code below into your new file:
import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';
const FunctionBasedComponent = () => {
return (
<Container className="user-list">
<h1>My Contacts:</h1>
</Container>
)
};
export default FunctionBasedComponent;
Now hop over to your App.js file, import your FunctionBasedComponent.js file and replace ClassBasedComponent with FunctionBasedComponent.
our starting useEffect app
Let’s start by initializing state with useState.
const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);
To quickly recap on useState, to initialize state with the useState hook, we declare both our variable and the function that corresponds to the variable in an array and the
The users state variable is initialized with an empty array and given the function of setUsers. The showDetails state variable is initialized with the value of false and assigned the function of setShowDetails.
Let’s go ahead and add in our API call as the fetchUsers function.
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
We are essentially pulling this async call from the former componentDidMount and componentDidUpdate functions.
Keep in mind we cannot use an async function directly inside useEffect. If we ever want to call an async function, we need to define the function outside of useEffect and then call it within useEffect.
Let’s talk about the useEffect hook for a moment. Much like componentDidMount, useEffect will immediately call our function.
useEffect( () => {}, [ 'value' ]);
By default, useEffect looks to see if the array values are different and if they are different, the arrow function is automatically called.
useEffect( () => {}, [ 'different value' ]);
Let’s flip back to our code editor and add the useEffect hook below our latest function where we will call fetchUsers.
In the code below, we’re looking at the users object to see if there are changes.
useEffect( () => { fetchUsers(users) }, [ users ] );
If you don’t pass an array into the useEffect Hook, your component will continuously reload repeatedly.
useEffect( () => { fetchUsers(users) } );
If you pass an empty array, we are not watching any variables, and therefore it will only update state on the first render, exactly like componentDidMount.
useEffect( () => { fetchUsers(users) }, [] );
Every time we create an object in JavaScript, it is a different object in memory. Though the code below looks the same, the page will be re-rendered because each object is
useEffect( () => { fetchUsers(users) }, [{ user: 'BeesCloud' }] );
Is not equal to! useEffect( () => { fetchUsers(users) }, [{ user: 'BeesCloud' }] );
useEffect function must return a cleanup function or nothing. To demonstrate triggering another re-render, copy and paste the code below into your FunctionBasedComponent.js file:
import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';
const FunctionBasedComponent = () => {
const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
useEffect( () => { fetchUsers(users) }, [ users ] );
const handleClick = event => { setShowDetails(!showDetails) };
return (
<Container>
{
users.map((user) => (
<ul key={ user.id }>
<li>
<strong>{ user.name }</strong>
<div>
<Button
onClick={ handleClick }
>
{ showDetails ? "Close Additional Info" : "More Info" }
</Button>
{ showDetails &&
<Container className="additional-info">
<Row>
{ `Email: ${ user.email }` }
</Row>
<Row>
{ `Phone: ${ user.phone }` }
</Row>
<Row>
{ `Website: ${ user.website }` }
</Row>
</Container>
}
</div>
</li>
</ul>
))
}
</Container>
)
}
export default FunctionBasedComponent;
Now we have an onClick event within a button. On the button click, the state of showDetails is changed, triggering a re-render that will call again to the API and bring in the additional details that we need.
async componentDidMount() {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
};
async componentDidUpdate(prevProps) {
if (prevProps.resource !== this.props.resource) {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
}
};
const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);
setUsers(response.data);
};
useEffect( () => { fetchUsers(users) }, [ users ] );
To shut-down and completely erase all
$ docker-compose down --rm all
Please whatsApp +65-8201-0159 or drop me an email at [email protected] for whatever reason? FREE STUFF : free office, online storage and AI at https://www.beesdot.com !
Content type
Image
Digest
Size
136.6 MB
Last updated
over 5 years ago
docker pull mltdocker/react-converting-to-a-hook_app