States and componentDidMount() in functional components with Hooks.

Tim Tan
3 min readApr 10, 2019

--

React 16.8 introduced the concept of Hooks and this blog aims to give a brief outline of their functionality and a brief introduction on how they’re to be used.

Prior to hooks you were required to use class based components if either a state or a lifecycle method was necessary . However, with this new feature functional components now have the ability have a states and lifecycle methods.

useState( )

Within a class Component the following syntax was used if a state was required:

import React, { Component } from 'react';

class Playlist extends Component {
state = { songs: [] }

songHandler = () => {
this.setState({ songs: ['The Chain'] } }

render() {
return (
<button onClick={this.songHandler}>
Add to Playlist
</button>
)
}
}

With react hooks we have the ability to use the state hook:

useState()

this is a function imported from the react package and to be called within a functional component. This gives functional components the ability to manage state.

import React, { useState } from 'react'const [state, setState] = useState(initialState)

useState( ) returns:

An array that always has 2 elements. The first element

  1. A stateful value (equals the value passed as initial state during the initial render initially)
  2. A function to update it
import React, { useState } from 'react'const Playlist= props => {
const [song, setSong] = useState([])

const songHandler = () => {
setSong(['The Chain'])
}

return <button onClick={songHandler}>Add to Playlist</button>
}

As you can see “this” isn’t required anymore as we aren’t within a class but a function.

Note: useState() works slightly differently to the way in which state and setState did within class-based components. When a new state is set, the old state will be replaced. Therefore, to update multiple state slices you would need to have multiple useState() calls. Unlike class components, you’re not limited to one state property.

import React, { useState } from 'react'const Playlist= props => {
const [song, setSong] = useState([])
const [artist, setArtist] = useState([])

const songHandler = () => {
setSong(['The Chain'])
setArtist(['Fleetwood Mac'])
}

return <button onClick={songHandler}>Add to Playlist</button>
}

Alternatively you can manually merge it by using the spread operator.

import React, { useState } from 'react'const Playlist= props => {
const [song, setState] = useState({
song: null,
artist: 'Fleetwood Mac'
})


const songHandler = () => {
setState({...state, song: ['The Chain']})
}

return <button onClick={songHandler}>Add to Playlist</button>
}

useEffect()

This Hook should be used for side-effects executed with the render cycle.

Usually within a class Component componentDidMount() is used in the following way:

componentDidMount() {
fetch(url)
.then(resp => resp.json())
.then(data => this.setState())
}

Prior to hooks, functional components had no access to lifecycle methods, if you wanted to make a http request you’d do so like this without componentDidMount().

const Songs = props => {
const [songs, setSongs] = useState([])
fetch(url)
.then(resp => resp.json())
.then(data => this.setState()
return()}

This is going to cause a number of problems:

  • On every re-render of <Songs />, a HTTP request will be sent which is unlikely to be wanted.
  • The state will be reset by the fetch.
  • Finally an infinite loop is executed as setSongs() will execute a re-render which leads to a fetch each time.

Welcome useEffect()!

useEffect() is to be used for side-effects executed in the render cycle. The previous code can now be written as:

const Songs = props => {
const [songs, setSongs] = useState([])
useEffect(() => {
fetch(url)
.then(resp => resp.json())
.then(data => this.setState()
})
return()}

How useEffect() works:

  • Takes in a function
  • Returns nothing
  • Executed after every render cycle (both the render and every re-render)

To prevent the function from being called unnecessarily and the issue of the infinite loop previously discussed a second argument is passed.

useEffect(() => {
fetch(url)
.then(resp => resp.json())
.then(data => this.setState()
}, [])

Passing [] as the second argument, useEffect to behave like componentDidMount as this tells useEffect that your effect isn’t dependent on any values from props or state thus never re-running. By default useEffect() would run on every update on the component but by passing an array of dependencies, in this case no dependencies so it never runs again.

By adding dependencies for example:

useEffect(() => {
fetch(url + songId)
.then(resp => resp.json())
.then(data => this.setSong()
}, [selectedSong])

Instead of passing through no dependencies we now pass selectedSong. By doing this when selectedSong changes the function shall be run again, this is essentially ComponentDidUpdate().

--

--