How to useState in React

Introduction

Since the additions of React Hooks in React 16.8, functional components allow for side effects and state management. Within state management in React, there are two hooks that can be utilized: useState and useReducer. This article will guide you on using useState for state management.

Mental Model

React ships with useState hook (function) and can be accessed through React.useState. The method takes a single argument, the initial value for that piece of state. It then returns an array, where the 0th index is the actual state and the 1st index is used to update that state.

The conventional and more precise way to write the above code is to use ES6 array destructuring, enabling us a one-liner.

Now that we’ve seen a simple example of how to utilize the useState API, we now know that useState allows us to trigger a component re-render and preserve values between those renders.

Component Re-render

Under the hood, when a new argument is passed into the current state value, React causes a re-render of the component, therefore updating the UI. In the above example, upon calling our setter setFruit and assuming that we’re passing in a new fruit value, React will re-render and show, for example, avocado or peach, depending on which fruit we’re currently seeing.

Preserving Values between Re-renders

When invoking a function in Javascript, any values defined in that function gets garbage collected once the function finishes executing. Each subsequent call to that function produces its own unique values unless you’re utilizing closures.

Since the beginning of React Hooks, react components are now functions. It may be natural for you to think the same. However, this is not the case. The entire philosophy of React is to ensure that components can illustrate their UI based on the current state. As a result, React has to have a way to persist values between function calls, preventing them from being garbage collected even after function invocation. The public API for this, as we have already seen, is useState.

To simply put, useState is an instrument to preserve values between function invocations/renders and to trigger component re-rendering.

useState

When using useState, it’s imperative to understand that each piece of the state comes with its own way to access the persisted value and updater function, following React’s composition pattern. If you’re coming from class components, you’ll find that the state management is nicely modularized as opposed to an object with multiple properties from setState.

It is also important to note that compared to setState in class components, useState does not merge the new object with the previous state. If you’re brand new to React, I would not worry about this. The important characteristic to understand is that if you have a previous piece of state and you’d like to have it merged to the new state, you’ll need to include a copy of it beforehand.

A more comprehensive example of setting the current state based on the previous state is illustrated below. In summary, you’ll pass a handler function to an event attribute, such as an onClick, which will call the updater function setFruit.

It may also be pertinent to utilize the useReducer hook if it so happens that the most logical data type is an object. That’s for another article, however!

Lazy State Initialization

There may be a scenario where you will need to initialize the state through a function call. To illustrate, it will look something like this.

If you were to play around with this code, you’ll notice that React uses the calculated value from getAvocadoCount on the initial render, as well as any subsequent re-renders. In other words, getAvocadoCount is invoked anytime the component re-renders — a computationally expensive calculation that can potentially result in a performance hit. This is not ideal at all as we would like for the initialization to be done once during the initial render only, not on subsequent renders. Fortunately, react gives us a way to navigate through this situation.

The solution is to pass useState a function that, when invoked, will resolve to the initial state. By doing so, useState sees that it received a function as the initial state argument, and it’ll only invoke it once on the initial render.

In summary, the solution lies in what we pass into useState. The first example illustrates when useState is passed a function invocation, whereas in the second example useState is initialized a function definition. By utilizing the function definition version, state will initialize in the first render and is disregarded in subsequent renders, resulting in performance optimization.

In Summary…

  • useState allows you to trigger a component re-render, add state to a function component, and preserve values between renders
  • enabling lazy state initialization is the result of passing in a function definition versus a function invocation

Software developer with a liking to avocados www.jayacados.com

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Preact meets CMS: Building Lightweight Portable Widget Components

Utilize Custom Keywords in Katalon Studio

All about Javascript (for interview)

Stop Using === Everywhere

Multi-party Video Conferencing Tool— Architecture & Design

Laravel whereDate and whereDay Example

Create Next.js Application and Deploy to Vercel

Run Salesforce Reports in Apex

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jimmy Phong

Jimmy Phong

Software developer with a liking to avocados www.jayacados.com

More from Medium

Understanding State and Props in React

Introduction to React

Coding in React

Components of React in 1 minute