DEV Community

Cover image for Create A Custom Hook In React
Barnabas Babatunde
Barnabas Babatunde

Posted on

Create A Custom Hook In React

In this article, we are going to cover the basics of React custom hooks and how to implement one.

Prerequisites:

A basic understanding of React functional components, React hooks, component props, ES6 destructuring.

What Is A React Custom Hook?

A React custom hook is a JavaScript function that allows you to share behavior (or logic) among other components (which are also JavaScript functions). As the name implies (custom), it is authored by you (the developer) and does not come with React library.

What Does A React Custom Hook Look Like?

  1. It is a JavaScript function
  2. Its name starts with use(by convention)
  3. It may invoke other hooks

Why Share Logic Using A Custom Hook?

Sharing logic among other components helps prevent code duplication. This becomes especially important for large applications, where it is necessary to abstract away some piece of logic into a single function (custom hook), and then use that function anywhere you want in your application (just as you would use a built-in React hook)

Let's Walk Through Some Code, Shall We?

Now we know what a custom hook is, let us go ahead and implement one.

Use Case:

Assuming we want to create a simple app to evaluate the monthly and yearly wage of an employee (James).

We create a functional component called Month which would display James' monthly wage on the browser, based on his years of experience. Month would also subtract from James' monthly wage depending on the number of work hours missed per month OR add to his monthly wage depending on the number of extra work hours per month.

We create a functional component called Annum which would display James' wage for one year, based on his years of experience. Annum would also subtract from James' yearly wage depending on the number of work hours missed per month (X12, for the entire year) OR add to his yearly wage depending on the number of extra work hours per month (X12, for the entire year).

The Month component would look like this:

import React, { useState } from 'react';

const Month = ({ name, years })=>{
    const [wage, setWage] = useState(years*3);
    const [extraHours, setExtraHours] = useState(0);
    const [missedHours, setMissedHours] = useState(0);

    const incrementWage = ()=>{
        setWage(wage + Number(extraHours));
    }

    const decrementWage = ()=>{
        setWage(wage - Number(missedHours));
    }

    return (
        <div>
            <h1>In One Month</h1>
            <p>{name}</p>
            <p>Monthly wage: {wage}</p>
            <label>
                Hours missed per month
                <input 
                value={missedHours}
                onChange={ (e)=> { setMissedHours(e.target.value)} }
                ></input>
            </label>
            <button onClick={ decrementWage }>Decrement Wage</button>

            <br/><br/>

            <label>
                Extra hours per month
                <input 
                value={extraHours}
                onChange={ (e)=>{ setExtraHours(e.target.value)} }
                ></input>
            </label>
            <button onClick={incrementWage}>Increment Wage</button>
        </div>
    )
}

export default Month;
Enter fullscreen mode Exit fullscreen mode

While the Annum component would look like this:

import React, { useState } from 'react'

const Annum = ({ name, years })=>{
    const [wage, setWage] = useState(years*3);
    const [extraHours, setExtraHours] = useState(0)
    const [missedHours, setMissedHours] = useState(0)

    const incrementWage = ()=>{
        setWage(wage + Number(extraHours))
    }

    const decrementWage = ()=>{
        setWage(wage - Number(missedHours))
    }

    return (
        <div>
            <h1>In One Year (12X)</h1>
            <p>{name}</p>
            <p>Yearly Wage: {wage*12}</p>
            <label>
                Hours missed per month
                <input 
                value={missedHours}
                onChange={ (e)=> { setMissedHours(e.target.value)} }
                ></input>
            </label>
            <button onClick={ decrementWage }>Decrement Wage</button>

            <br/><br/>

            <label>
                Extra hours per month
                <input 
                value={extraHours}
                onChange={ (e)=>{ setExtraHours(e.target.value)} }
                ></input>
            </label>
            <button onClick={incrementWage}>Increment Wage</button>
        </div>
    )
}

export default Annum
Enter fullscreen mode Exit fullscreen mode

The App component will import both the Month and Annum components and pass in name and years props:

import React from 'react';
import Month from './Month';
import Annum from './Annum';


const App = ()=>{

  return (
    <div className="App">
      <Month name="James" years={10}/>

      <br/><br/><br/>

      <Annum name="James" years={10}/>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Outputs on browser:
Alt Text
Alt Text

Notice that Month and Annum have some common, shared functionalities just before their respective return statements. This is where a React custom hook comes in.

We can extract these common functionalities into a third function (custom hook), and use it in the Month and Annum components. Doing so would make the consumer components look much cleaner. Also, Month and Annum won't have to know how wage is evaluated, incremented, or decremented - they simply focus on rendering UI to the browser, and let the custom hook take care of the logic.

Our custom hook will be named useWage and it will look like this:

import React, { useState } from 'react';

const useWage = (years)=>{
    const [wage, setWage] = useState(years*3);
    const [extraHours, setExtraHours] = useState(0);
    const [missedHours, setMissedHours] = useState(0);

    const incrementWage = ()=>{
        setWage(wage + Number(extraHours));
    }

    const decrementWage = ()=>{
        setWage(wage - Number(missedHours));
    }

    const incrementHours = (e)=>{
        setExtraHours(e.target.value);
    }

    const decrementHours = (e)=>{
        setMissedHours(e.target.value);
    }



    return {
        wage,
        extraHours,
        missedHours,
        incrementWage,
        decrementWage,
        incrementHours,
        decrementHours
    }
}

export default useWage;
Enter fullscreen mode Exit fullscreen mode

Notice that useWage has two functionalities: incrementHours and decrementHours in addition to the extracted functionalities, making it easier to change the number of extra hours and missed hours per month in our Month and Annum components' input fields.

Our Month component would then look like this:

import React from 'react'
import useWage from './useWage'

const Month = ({ name, years })=>{
    const {
        wage,
        extraHours,
        missedHours,
        incrementWage,
        decrementWage,
        incrementHours,
        decrementHours
    } = useWage(years);


    return (
        <div>
            <h1>In One Month</h1>
            <p>{name}</p>
            <p>Monthly wage: {wage}</p>
            <label>
                Hours missed per month
                <input 
                value={missedHours}
                onChange={ (e)=> { decrementHours(e)} }
                ></input>
            </label>
            <button onClick={ decrementWage }>Decrement Wage</button>

            <br/><br/>

            <label>
                Extra hours per month
                <input 
                value={extraHours}
                onChange={ (e)=>{ incrementHours(e)} }
                ></input>
            </label>
            <button onClick={incrementWage}>Increment Wage</button>
        </div>
    )
}

export default Month;
Enter fullscreen mode Exit fullscreen mode

While our Annum component would look like this:

import React from 'react'
import useWage from './useWage'

const Annum = ({ name, years })=>{
    const {
        wage,
        extraHours,
        missedHours,
        incrementWage,
        decrementWage,
        incrementHours,
        decrementHours
    } = useWage(years);

    return (
        <div>
            <h1>In One Year (12X)</h1>
            <p>{name}</p>
            <p>Yearly Wage: {wage*12}</p>
            <label>
                Hours missed per month
                <input 
                value={missedHours}
                onChange={ (e)=> { decrementHours(e)} }
                ></input>
            </label>
            <button onClick={ decrementWage }>Decrement Wage</button>

            <br/><br/>

            <label>
                Extra hours per month
                <input 
                value={extraHours}
                onChange={ (e)=>{ incrementHours(e)} }
                ></input>
            </label>
            <button onClick={incrementWage}>Increment Wage</button>
        </div>
    )
}

export default Annum;
Enter fullscreen mode Exit fullscreen mode

Notice that Month and Annum look much cleaner than earlier before.

The outputs on the browser remain the same because we have not made any change to the functionalities. We only extracted repeated code into a separate third function.

Alt Text
Alt Text

Conclusion.

We have learned what a React custom hook is, the benefits, and how to implement a basic custom hook. I hope you can add this to your coding tool box and become a better React developer. Peace ✌

Discussion (4)

Collapse
lukeshiru profile image
LUKESHIRU

Dev.to tip: If you put the language next to the triple quote in the code blocks, you get syntax highlight. So it will look something like this if you put jsx next to the triple quote:

const Component = props => <div {...props} />
Enter fullscreen mode Exit fullscreen mode
Collapse
barnabas19 profile image
Barnabas Babatunde Author

Thanks a lot. I will do that next time.

Collapse
ayeprahman profile image
Arif Rahman

Great article

Collapse
barnabas19 profile image
Barnabas Babatunde Author

Thank you Arif.