This series will cover the use of React Hooks, starting with useState, and will include the following:

  • useState
  • useEffect
  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • custom hooks

Learn the React Hooks API to help you use React in your work. This series uses a lot of sample code and effect demonstrations, making it easy for beginners and refreshers to use.

Having learned so much about the official hooks API so far, we can create hooks of our own, and even developers are encouraged to abstract component logic into custom hooks for easy reuse.

A custom Hook is a function whose name starts with “use” that can call other hooks from within.

With custom hooks, component logic can be extracted into reusable functions.

UseDocumentTitle sample

Function = function

We want to create a counter, and when the value of the counter changes, we want to change the Title of the page

DocTitleOne.tsx

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

function DocTitleOne() {
  const [count, setCount] = useState(0)
  useEffect(() = > {
    document.title = `Count - ${count}`
  }, [count])
  return (
    <div>
      <button
        onClick={()= > {
          setCount(count + 1)
        }}
      >Count - {count}</button>
    </div>)}export default DocTitleOne

Copy the code

App.tsx

import React from 'react'
import './App.css'

import DocTitleOne from './components/31DocTitleOne'

const App = () = > {
  return (
    <div className="App">
      <DocTitleOne />
    </div>)}export default App
Copy the code

The page display is as follows

It worked fine, and then there was another incremental requirement that the page should be able to change the Title of the page in another component, so we created a new component.

DocTitleTwo.tsx

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

function DocTitleTwo() {
  const [count, setCount] = useState(0)
  useEffect(() = > {
    document.title = `Count - ${count}`
  }, [count])
  return (
    <div>
      <button
        onClick={()= > {
          setCount(count + 1)
        }}
      >Count - {count}</button>
    </div>)}export default DocTitleTwo
Copy the code

App.tsx

import React from 'react'
import './App.css'

import DocTitleOne from './components/31DocTitleOne'
import DocTitleTwo from './components/31DocTitleTwo'

const App = () = > {
  return (
    <div className="App">
      <DocTitleOne />
      <DocTitleTwo />
    </div>)}export default App
Copy the code

The page display is as follows

Reviewing the code, DocTitleTwo clearly duplicates DocTitleOne’s code, which you don’t want to repeat if you have 10 components that change the page title. This is where custom hooks are needed.

In this example, we can create a custom Hook to set the title of the page. Then use this custom Hook in different components.

Abstract useDocumentTitle hook

useDocumentTitle.tsx

import { useEffect } from 'react'

function useDocumentTitle(count: number) {
  useEffect(() = > {
    document.title = `Count - ${count}`
  }, [count])
}

export default useDocumentTitle
Copy the code

DocTitleOne.tsx

import React, { useState } from 'react'
import useDocumentTitle from './hooks/useDocumentTitle'

function DocTitleOne() {
  const [count, setCount] = useState(0)
  useDocumentTitle(count)
  return (
    <div>
      <button
        onClick={()= > {
          setCount(count + 1)
        }}
      >Count - {count}</button>
    </div>)}export default DocTitleOne
Copy the code

DocTitleTwo.tsx

import React, { useState } from 'react'
import useDocumentTitle from './hooks/useDocumentTitle'

function DocTitleTwo() {
  const [count, setCount] = useState(0)
  useDocumentTitle(count)
  return (
    <div>
      <button
        onClick={()= > {
          setCount(count + 1)
        }}
      >Count - {count}</button>
    </div>)}export default DocTitleTwo
Copy the code

App.tsx

import React from 'react'
import './App.css'

import DocTitleOne from './components/31DocTitleOne'
import DocTitleTwo from './components/31DocTitleTwo'

const App = () = > {
  return (
    <div className="App">
      <DocTitleOne />
      <DocTitleTwo />
    </div>)}export default App
Copy the code

The page display is as follows

Let’s review the code

In DocTitleOne, we introduce the useDocumentTitle we defined, passing in the count state. UseDocumentTitle executes the code, sets the page title initial value to 0, and continues rendering the DocTitleOne JSX section. When the button is clicked, count changes to 1, triggering Rerender of DocTitleOne, useDocumentTitle changes to 1, and the page title changes to 1.

UseCounter sample

Redundant writing

CounterOne.tsx

import React, {useState} from 'react'

function CounterOne() {
  const [count, setCount] = useState(0)
  const increment = () = > {
    setCount(prevCount= > prevCount + 1)}const decrement = () = > {
    setCount(prevCount= > prevCount - 1)}const reset = () = > {
    setCount(0)}return (
    <div>
      <h2>Count - {count}</h2>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
      <button onClick={reset}>reset</button>
    </div>)}export default CounterOne
Copy the code

CounterTwo.tsx

import React, {useState} from 'react'

function CounterTwo() {
  const [count, setCount] = useState(0)
  const increment = () = > {
    setCount(prevCount= > prevCount + 1)}const decrement = () = > {
    setCount(prevCount= > prevCount - 1)}const reset = () = > {
    setCount(0)}return (
    <div>
      <h2>Count - {count}</h2>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
      <button onClick={reset}>reset</button>
    </div>)}export default CounterTwo
Copy the code

App.tsx

import React from 'react'
import './App.css'
import CounterOne from './components/32CounterOne'
import CounterTwo from './components/32CounterTwo'

const App = () = > {
  return (
    <div className="App">
      <CounterOne />
      <CounterTwo />
    </div>)}export default App
Copy the code

The page display is as follows

We have a lot of duplicate code for the same problem, so let’s see how to optimize it using custom hooks.

UseCounter abstract

useCounter.tsx

import { useState } from 'react'

function useCounter() {
  const [count, setCount] = useState(0)
  const increment = () = > {
    setCount(prevCount= > prevCount + 1)}const decrement = () = > {
    setCount(prevCount= > prevCount - 1)}const reset = () = > {
    setCount(0)}return [count, increment, decrement, reset]
}

export default useCounter
Copy the code

CounterOne.tsx

import React from 'react'
import useCounter from './hooks/useCounter'

function CounterOne() {
  const [count, increment, decrement, reset] = useCounter()
  return (
    <div>
      <h2>Count - {count}</h2>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
      <button onClick={reset}>reset</button>
    </div>)}export default CounterOne
Copy the code

CounterTwo.tsx

import React from 'react'
import useCounter from './hooks/useCounter'

function CounterTwo() {
  const [count, increment, decrement, reset] = useCounter()
  return (
    <div>
      <h2>Count - {count}</h2>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
      <button onClick={reset}>reset</button>
    </div>)}export default CounterTwo
Copy the code

App.tsx

import React from 'react'
import './App.css'
import CounterOne from './components/32CounterOne'
import CounterTwo from './components/32CounterTwo'

const App = () = > {
  return (
    <div className="App">
      <CounterOne />
      <CounterTwo />
    </div>)}export default App
Copy the code

The page display is still as follows

As you can see, our code structure is better now. We can also set the initial value for counter in useCounter, as follows

useCounter.tsx

import { useState } from 'react'

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue)
  const increment = () = > {
    setCount(prevCount= > prevCount + 1)}const decrement = () = > {
    setCount(prevCount= > prevCount - 1)}const reset = () = > {
    setCount(initialValue)
  }
  return [count, increment, decrement, reset]
}

export default useCounter
Copy the code

When used, the corresponding initial value can be passed

const [count, increment, decrement, reset] = useCounter(10)
Copy the code

We can also modify each increment or subtraction as follows

import { useState } from 'react'

function useCounter(initialValue = 0, value = 1) {
  const [count, setCount] = useState(initialValue)
  const increment = () = > {
    setCount(prevCount= > prevCount + value)
  }
  const decrement = () = > {
    setCount(prevCount= > prevCount - value)
  }
  const reset = () = > {
    setCount(initialValue)
  }
  return [count, increment, decrement, reset]
}

export default useCounter
Copy the code

When used, the corresponding input parameter can also be added

const [count, increment, decrement, reset] = useCounter(10.5)
Copy the code

UseInput sample

The example is a simple form in which the user can fill in a name

Function = function

UserForm.tsx

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

function UserForm() {
  const [firstName, setFirstName] = useState(' ')
  const [lastName, setLastName] = useState(' ')
  const submitHandler = (e: FormEvent) = > {
    e.preventDefault()
    console.log(`Hello ${firstName} ${lastName}`)}return (
    <div>
      <form onSubmit={submitHandler}>
        <div>
          <label htmlFor="">First name</label>
          <input
            type="text"
            value={firstName}
            onChange={(e)= > {
              setFirstName(e.target.value)
            }}
          />
        </div>
        <div>
          <label htmlFor="">Last name</label>
          <input
            type="text"
            value={lastName}
            onChange={(e)= > {
              setLastName(e.target.value)
            }}
          />
        </div>
        <button>submit</button>
      </form>
    </div>)}export default UserForm
Copy the code

App.tsx

import React from 'react'
import './App.css'

import UserForm from './components/33UserForm'

const App = () = > {
  return (
    <div className="App">
      <UserForm />
    </div>)}export default App
Copy the code

Abstract useInput hook

useInput.tsx

import { useState } from 'react'

function useInput(initialValue: string) {
  const [value, setValue] = useState(initialValue)
  const reset = () = > {
    setValue(initialValue)
  }
  const bind = {
    value,
    onChange(e: any) {
      setValue(e.target.value)
    }
  }
  return [value, bind, reset]
}

export default useInput
Copy the code

UserForm.tsx

import React, { FormEvent } from 'react'
import useInput from './hooks/useInput'

function UserForm() {

  const [firstName, bindFirstName, resetFirstName] = useInput(' ')
  const [lastName, bindLastName, resetLastName] = useInput(' ')

  const submitHandler = (e: FormEvent) = > {
    e.preventDefault()
    console.log(`Hello ${firstName} ${lastName}`)
    // @ts-ignore
    resetFirstName()
    // @ts-ignore
    resetLastName()
  }
  return (
    <div>
      <form onSubmit={submitHandler}>
        <div>
          <label htmlFor="">First name</label>
          <input
            type="text"
            {. bindFirstName} / >
        </div>
        <div>
          <label htmlFor="">Last name</label>
          <input
            type="text"
            {. bindLastName} / >
        </div>
        <button>submit</button>
      </form>
    </div>)}export default UserForm
Copy the code

The page display

summary

This chapter focuses on custom hooks, with three examples to help us learn abstract and reuse code. There are also many people in the community who have written their own custom hooks that you can go and learn. You are also encouraged to create your own custom hooks.

This concludes the series. Good luck. We’ll all learn something.