How to Use State in Functional Components in React 16.8

React 16.8 gives developers much more freedom when it comes to functional components.

React 16.8 will change the landscape of React components forever.
React 16.8 will change the landscape of React components forever.

Small and lightweight functional components have only had a subset of the features available to components made with classes. That is until now. In addition to allowing state, functional components have access to the equivalent of component lifecycle methods as well. This allows the same amount of control while requiring less code.

In React 16.8 they added features that functional components have been lacking for quite some time. In addition, more functionality which have been the domain of 3rd party libraries such as Redux, also get baked right in.

This article will take a look at how to use state and lifecycle methods inside functional components to reduce the complexity and repetition of your code. In a later article I will also cover contexts and how to avoid prop drilling. Let’s get started.

State in Functional Components

Prior to React 16.8, if we wanted a component to have state it needed to be written as a class. Now we can use state in functional components as well. First you’ll need to import useState from react.

import React, { useState } from 'react';

useState is a function. This is what we’ll need in order to add state to our functional component.

const [count, setCount] = useState(0);

useState is called with the initial value that state should be set at. In the line above we’re setting our initial count value to 0.

What is returned when we call useState is an array where the first element is the value for state and the second element is a setter for it.

The example above uses Javascript array destructuring to set the state value to count and the setter to setCount.

Using State

When you want to use state you can just reference the value in the state variable you chose. In the above example it is count. You can use it inside the JSX output of your function by simply using the curly braces: {count}

<p>Current Count: {count}</p>

In order to update our count we can just make a call to our setter setCount.

<button onClick={ ()=> setCount(count + 1)}>Change Count</button>

So far an entire functional component with state looks like this:

import React, { useState } from 'react';

function Counter(){
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Current Count: {count}</p>
      <button onClick={()=> setCount(count + 1)}>Change Count</button>
    </div>
  )
}
export default Counter;

Lifecycle Methods in Functional Components

In addition to state, we can also have the same control that we did with all those lifecycle methods only now in our functional components. Because it’s often required to duplicate code across several component lifecycle methods, React developers devised a clever, simpler system to use.

This is where we introduce useEffect.

import React, { useEffect } from 'react';

The way to use this new function may not be entirely obvious at first but I’ll walk through the various ways to use it effectively.

The basic use case is here:

useEffect(()=>{
  // This gets run as you would expect with componentDidUpdate
  // and componentDidMount
})

Call useEffect with the initial value you want your state to be. It takes care of all the code that you would normally put into the compontDidUpdate and componentDidMount methods but instead consolidates it into a single function thereby reducing the amount of code your application needs.

While this may be useful for most of your applications, if you only want to run code when a particular value changes then you can pass in a second parameter — an array with the state to watch. In addition, any stateful values your code needs to access should be passed in this way to ensure that your code has access to the latest value.

useEffect(()=>{
  // This is run only when the values for state that are passed in change.
  // All state used by this function must be passed in together
  // to insure that it is current.
}, [someStateValue, someOtherStateValue])

Cleanup

Sometimes you need to do an action when your component unmounts such as remove listeners. useEffect gives us a way to do this as well.

The function that is passed into useEffect must either return a function or else nothing at all.

The returned function is only called when the component unmounts.

Here’s an example.

useEffect(()=>{
  // This is called when the component updates.
  return ()=>{
    // This anonymous function gets called when the component will unmount.
    // This is where you would remove any listeners etc...
  }
}) 

Additional Features

In addition to adding features to functional components, React 16.8 takes aim at Redux by building within React ways to store state globally that prevent us from needing to prop drill down into deeply nested components.

The next article will go over how to use Context in React to replace the features of Redux.

Leave a Reply