Optimizing Your Components with React Hooks

Optimizing Your Components with React Hooks

If you began using the React framework prior to version 16.8, you would agree that using class components was difficult. While using class components makes it difficult to reuse stateful logic, the presence of hooks makes it very easy to share logic between components and write simpler, more readable code.

Whether you're a seasoned React developer or just getting started, I'll walk you through some essential React hooks and practical examples.

What are React Hooks?

React Hooks is a new React feature that enables the addition of state and other React features to functional components. Only class components could have state and lifestyle methods prior to the introduction of React hooks. We can now make our components more reusable, improve performance, and write more readable code thanks to React hooks. React hooks were introduced in version 16.8 and have since become an essential component of React and its users.

In this tutorial, we will look at the various types of React hooks, which are as follows:

  • useState

  • useEffect

  • useContext

  • useReducer

Benefits of React hooks

The presence of React hooks has made a developer's job easier. They provide a number of advantages that make writing clean, efficient, and maintainable code easier. Let's look at the advantages of React hooks that will convince you to start using them in your applications.

  • Enhanced Functionality: With React hooks, you can add new features and capabilities to your components, making it easier to build complex and dynamic user interfaces.

  • Improved Organization: Hooks facilitate the organization and management of state and component logic, resulting in more readable code.

  • Code Reusability: React hooks allow you to share stateful logic across multiple components, making code reuse easier.

Now that we've established what React hooks are, examples of React hooks, and the benefits of React hooks, let's dive into the examples of React hooks and their practical applications one by one.

The useState hook

useState is a built-in React hook for adding state to functional components. Do you understand how variables are defined and then used in components? useState is similar to that, except you can set and change variables.

Before you can use the useState hook in your component, you must import it at the top of your component, where you have the import react.

import React, { useState } from 'react'

The useState function returns an array with two elements: the current state value, name in this case, and a function setName to update the state value. The state value can be an integer, a boolean, a string, an object, an array, or any combination of these.

The useState syntax

The following step would be to write out the useState syntax. The name of the state value already has a default value. It could be empty.

const [name, setName] = useState('yam')

When you return name in your code, your browser will display "yam" because that is the default value.

 return (
    <div>
      <h6>{name}</h6>
    </div>
  )

useState example

Consider a more concrete example using useState. In this example, we would update the name from an array at random. I made an array of foods as well as a function that changes the name to a random food.

const food = ['Beans', 'potatoes', 'Rice', 'Eba']
  const rand = () => {
    setName(food[Math.floor(Math.random() * 4)])
  }

The rand function sets the name of food from the food array at random. We'll use an onClick event so that when we click on the name value in our browser, it changes to any string in the food array at random.

return (
    <div>
      <h6 onClick={() => rand()}>{name}</h6>
    </div>
  )

We can see from this example that using the useState hook allows us to set and update a value.

The useEffect hook

useEffect is a built-in React Hook that allows you to fetch data, update the DOM, and set timers. useEffect has two arguments, which are as follows:

  • Callback function: This function contains logic for fetching data, setting timeouts, adding or removing elements from an array, and updating a state.

  • Dependency array: The dependency array is used to determine when to re-run the side effect. If the dependency array is empty, the side effect will only run once, but if it is not empty, it will re-run whenever the values in the dependency array change.

The useEffect syntax

Because the dependency array can be empty or not, I'll demonstrate this with examples.

We have the following syntax for an empty array:

useEffect(() => {}, [])

Because this is a useEffect hook with no callback function, add a function that increases the count value by one.

const [count, setCount] = useState(0)

  useEffect(() => {
    setCount(count + 1)
  }, [])

This callback function simply increments the count value by one. Because the dependent array is empty, the side effect will only run once and then stop. This means that the value displayed in your browser will be 1.

However, if the dependent array is not empty and is occupied by count, the side effect will continue to run. This also occurs when no dependency array is passed.

useEffect(() => {
    setCount(count + 1)
  }, [count])

Now that we know what useEffect is, let's make an API call with the useEffect hook and render it on our browser.

useEffect example

Let us not forget to import useEffect in the component we will make use of.

import React, { useState, useEffect } from 'react'

We also imported useState because we will be using it in the useEffect function to set and update a state.

const [data, setData] = useState([])

  useEffect(() => {
    fetch('https://api.adviceslip.com/advice')
      .then((response) => response.json())
      .then((data) => setData([data]))
      .catch((error) => console.error(error))
  }, [])

The useEffect hook's callback function makes an API call, and the setData function updates data based on the API response.

As you may have noticed, we have an empty dependency array that will only render the API once. To return the contents of the API to your browser, use the code snippet below.

Because the API is set to be an array, we will map the data value to get the API contents.

return (
    <div>
      {data.map((item) => {
        return (
          <div>
            <p>{item.slip.id}</p>
            <p>{item.slip.advice}</p>
          </div>
        )
      })}
    </div>
  )

The useContext hook

You can use useContext to pass a state or data globally without going through the props process. useContext allows components to easily share state or data. For example, suppose you want to pass a state from a parent component to a child component or use a state in a component that is not the child component; how would you get the data? You don't have to declare a state or function multiple times in different components when you use useContext. All you need to do is define the state in your context file and share it with any component that requires it. This simplifies the coding process.

How to make use of useContext

To be able to access useContext in any of your components, you need to do the following:

Create a context file

To create a context file, follow these steps:

  • In the src folder of your project, create a folder and name it contexts.

  • In the contexts folder, create a file and name it StoreContext.js. Keep in mind that you can name your file whatever you want.

  • In the StoreContext.js file, write this code snippet below

import React, { createContext } from 'react'

export const StoreContext = createContext()

const StoreContextProvider = ({ children }) => {

  return (
    <StoreContext.Provider
      value={{

      }}
    >
      {children}
    </StoreContext.Provider>
  )
}

export default StoreContextProvider

The preceding code snippet is a boilerplate of what should be in the context file before adding anything else. Stay tuned as I explain the boilerplate.

The first is the createContext. The createContext we're importing simply creates a new context for data sharing between components.

import React, { createContext } from 'react'

The code snippet below uses the createContext function to create and export StoreContext. This means that you can use the content of StoreContext in any component of your choice.

export const StoreContext = createContext()

Before we get started with an example, let me explain what the StoreContext.Provider does. The following code snippet creates a context provider that wraps its children and provides data to them via the context. In this case, the children are any of the components to which we want to send data.

<StoreContext.Provider value={{ count, setCount }}>
   {children}
</StoreContext.Provider>

I hope we can recognize how count and setCount came about from the code snippet above.The value prop is set to an object containing the count and setCount state values, allowing our components to access them.

Let's do one more thing before we move on to an example. Next, we'll wrap StoreContextProvider around the App.js component. This is critical because, even after you create a context file, your states or functions cannot be used globally in any component until your context provider is wrapped around your App.js component. So, in your index.js component, add the following:

import StoreContextProvider from './contexts/StoreContext'

<StoreContextProvider>
  <App />
</StoreContextProvider>

useContext example

In this example, we will create a StoreContext file and define a function that will update the state by one, then pass the state and function to any component we want.

const StoreContextProvider = ({ children }) => {
  const [count, setCount] = useState(0)
  const add = () => {
    setCount(count + 1)
  }

  return (
    <StoreContext.Provider value={{ count, add }}>
      {children}
    </StoreContext.Provider>
  )
}

Let's move on to the component that requires the count state and the add function.

To access the data provided by the context, we must import StoreContext into the component.

import { StoreContext } from './contexts/StoreContext'

Now that we've imported the StoreContext, we'll use the useContext hook to return the data that we passed into our component from the StoreContext.

  const { count, add } = useContext(StoreContext)

The code in the previous snippet utilizes destructuring assignment to retrieve the count and add values from the object returned by the useContext hook.

Use the following code to display the count value:

return (
    <div>
      <p onClick={add}>count</p>
      <h1>{count}</h1>
    </div>
  )

When the p element is clicked, the onClick event handler is called, and it is defined using the add value extracted from the context data using the useContext hook. As a result, every time you click count in the browser, the count value increases by one.

The useReducer hook

useReducer, like the useState hook, is used for state management, but it is more useful for managing complex state logic.

The useReducer syntax

The usereducer hook takes two parameters: a reducer and an initial state value. A reducer is a function that takes as inputs the current state and an action and returns the new state based on the action. The initialState value represents the state's starting value and is used to initialize the state when the component is first rendered.

const [state, dispatch] = useReducer(reducer, initialState);

As previously stated, the reducer function accepts state and the dispatch function and returns a new state that has been updated by the dispatch function.

useReducer example

The useReducer hook is demonstrated in the example below. To manage a counter state, this example uses useReducer.

import React, { useReducer } from 'react'

const App = () => {
  const initialState = { count: 0 }
  const [state, dispatch] = useReducer(reducer, initialState)

  function reducer(state, action) {
    if (action.type === 'increment') {
      return { count: state.count + 1 }
    } else if (action.type === 'decrement') {
      return { count: state.count - 1 }
    } else {
      return state
    }
  }

  return (
    <div>
      <p onClick={() => dispatch({ type: 'increment' })}>Increase</p>
      <p onClick={() => dispatch({ type: 'decrement' })}>Decrease</p>
      <h1>{state.count}</h1>
    </div>
  )
}

export default App

In the preceding example, we began with an import because that is the first thing you should do when attempting to access any React hook. The next step was to was initialize the count state to zero. The reducer function accepts two parameters: state and action. There is an if-statement in the function that checks the type of action to perform when the onClick event handler is triggered, then updates the count state via the dispatch function. So, if the action was increment, the count increases by one, but if the action was decrement, the count decreases by one.

Conclusion

You learned in this tutorial that using the useState and useEffect hooks allows you to manage the state and side effects of your components in a more efficient and readable manner. UseContext to share state across your components globally, and useReducer to manage complex state logic.