My Commonly Used React Hooks

My Commonly Used React Hooks

Muhamad Amir Azmi
Muhamad Amir Azmi
3 November 2023
tutorial

React hooks are the common way to interact with the component in various behaviour. Before React hooks being introduced, we are exposed with component lifecycle and component state with class based component. Now, we can do the same thing with React hooks.

From Official Docs

There are a lot of React hooks that developer can used in React. Each hooks has its own functionality which makes developer life easier. For example, useState and useReducer makes the component state management easier instead of using var, let or const to hold our component state.

In official docs also show us how to create our own custom hooks which can abstract away our component logic from our rendering component. Custom hooks also can contains built in React hooks.

Can read more details on React hooks from the official documentation. Here I just want to share my most commonly used React hooks that I use everyday in every React project:

useState - Handle Component State

In every React app that I have built, there is always a need to track and keep state or data changes within the component. With useState, instead of keeping the state with var, let and const which not a good thing to do in React, I will use useState. There are few ways of using useState.

  1. For toggle - modal, popup, hide/show

  2. Form input data store

  3. Store manipulated data

Toggle

The most common usage of useState is use as toggle. We can create a state which holds boolean value either true or false and conditionally renders the component depends on the state boolean value.

For example, we can use useState to handle the toggle of popup component as below.

const [show, setShow] = useState(false)

if(show) return (
  <div>
    <h1>Show Popup</h1>
    <span onClick={()=>setShow(!show)}>Close</span>
  </div>
)

if(!show) return (
  <button onClick={()=>setShow(!show)}>Open Popup</button>
)

From this code, we can refactor by extracting the click handle and put it into separate reusable function. This will make our component more optimize since the application do not need to recreate anonymous function each time our component re-renders.

const [show, setShow] = useState(false)

const toggle = () => setShow(!show)

if(show) return (
  <div>
    <h1>Show Popup</h1>
    <span onClick={toggle}>Close</span>
  </div>
)

if(!show) return (
  <button onClick={toggle}>Open Popup</button>
)

We also can use toggle useState to hold our loading state. For example, when the component is fetching some data when user click on a button, we want to show some loading state or loading message to user so that user understand that the data fetching is happening especially when the data fetching takes quite some time to complete.

const [data, setData] = useState([])
const [loading, setLoading] = useState(false)

const getNewData = async () => {
  setLoading(true)
  const newData = await fetch('...')
  setData(newData)
  setLoading(false)
}

if(loading) return (
  <div>Application is fetching new data..</div>
)

if(!loading) return (
  <div>
    <div>
      {data.map((item, k)=><div key={k}>{item.name}</div>)}
    </div>
    <button>Fetch New Data</button>
  </div>
)

The code above shows that, on initial component load, there is no items in data array. The component only renders a button with Fetch New Data label. The initial loading state is false.

Then, when user click on the button, the loading state changed to true which will let the component renders the Application is fetching new data.. text on the screen while the application is fetching the new data.

After quite some time, the data state updated and the loading state changed to false which will let the component renders the original template with a list of item.name on the screen.

These are two quick example how to use useState as toggle state. There are a lot of situation where we might need to use two to five toggle state in one component depends on how complicated the component is.

Form Input Data Store

In any web application, there must have some sort of form input component such as register and login form. The input from the form need to be store somewhere before being sent to the backend API. Typically, we can store the input value in the state using useState. Here is the example how we can achieve that.

const [input, setInput] = useState({})

const handleChange = (e) => {
  setInput([e.target.name]: e.target.value)
}

const handleSubmit = async () => {
  await fetch(..., {
    method: 'POST',
    data: input
  })
}

return (
  <form onSubmit={handleSubmit}>
    <input name="name" type="text" onChange={handleChange}/>
    <button>Submit</button>
  </form>
)

The input state above will be updated each time user insert any text input into input field through onChange handler. The input state object key name comes from the name attribute of the input through [e.target.name] and the value for the name key is coming from e.target.value.

Then, when user click the submit button, the handleSubmit will trigger and submit the input to the fetch or any third party API. This is simple example how we can use useState to store our input data before being process and passed to API. Usually, the input state may have 3 to 5 different key value pairs which the values are coming from the input fields.

Store Manipulated Data

When we loop an array, either through component props or fetching the data from external API, we always need to manipulated the item in the array to make sure we only store the data that we need.

For example, the item in the array do not have a slug or an id after do the data fetching. We cannot simply loop over the array if our JSX template need slug and id to work well. So, we need to do data manipulation either in useEffect or after do the data fetching and store a new array in a state using useState.

const [data, setData] = useState()

useEffect(()=>{
  const fetchData = async () => {
    const res = await fetch(...)
    const newData = res.map((item) => {
      const i = item
      i.id = Math.random()
      return i
    })
    setData(newData)
  }
},[])

if(!data) return null
return (
  <div>{data.map((item, k) => <div key={k}>{item.id}</div>)}</div>
)

From the example above, on component load, inside the useEffect, the data has been fetched and it seem the fetched data do not have id property which will be needed inside the JSX. So, the response from the fetched data will be mapped over and add additional id property.

After that, the newData returned by map loop will be used to update the data state through setData function. This is one of the example how we can use useState to store our manipulated data.

useState Gotcha - Remember This

Each time the state changes, the component will be rerender. Make sure to use useState when neccessery and component rerender is needed. This is because, when component is rerender due to state change, some of things might not work well. CSS animation for example, one of things that I always encounter is that, the animation does not work well because some of the JSX depending on state to hide and show.

This can be demonstrate with building simple popup modal with some ease in out animation. If the modal JSX depends on toggle state directly, it will just show and hide without any animation due to the missing HTML element inside HTML DOM.

useRef - Referencing DOM Element

useEffect - Handle The Component Event