React Hooks API

Most common React Hooks: useState, useEffect, useContext, useReducer

React

React Hooks

Javascript

Web Development

Table of Contents

1. useState()

useState() has two parameters: the first is the value of the state, and the second is the function that manages the state.

import React, { useState } from "react"

const App = () => {
  const [authForm, setAuthForm] = useState({
    username: "",
    password: "",
  })

  const handleChange = event => {
    const { name, value } = event.target
    setAuthForm(prevState => ({
      ...prevState,
      [name]: value,
    }))
  }

  return (
    <>
      <input
        name="username"
        value={authForm.username}
        onChange={handleChange}
      />
      <input
        name="password"
        value={authForm.password}
        onChange={handleChange}
      />
    </>
  )
}

export default App

1.1 Input Form Hooks Demo

For example, the form authForm above can be encapsulated into a custom form logic hook specifically to handle form inputs.

useFrom.js

import { useState } from "react"

const useForm = initState => {
  const [form, setForm] = useState(initState)

  return [
    form,
    event => {
      setForm({
        ...form,
        [event.target.name]: event.target.value,
      })
    },
  ]
}

export default useForm

App.js

Since we return an array in useForm, the naming doesn't need to match the one in useForm. We access the corresponding parameters using the index. (Index 0 is the state, and index 1 is the function that manages the state.)

import React, { useState } from "react"
import { useForm } from "./useForm"

export const App = () => {
  const [authForm, setAuthForm] = useForm({
    username: "",
    email: "",
    password: "",
  })

  return (
    <>
      <input name="username" value={authForm.username} onChange={setAuthForm} />
      <input name="password" value={authForm.password} onChange={setAuthForm} />
      <input name="email" value={authForm.email} onChange={setAuthForm} />
    </>
  )
}

1.2 Counter Demo

useCounter.jsx

import React, { useState } from "react"

const useCounter = initState => {
  const [count, setCount] = useState(initState)

  const increment = (payload = 0) => {
    setCount(prevCnt => prevCnt + payload)
  }

  return [count, increment]
}

export default useCounter

App.jsx

import React, { useState } from "react"
import useCounter from "./useCounter"

const App = () => {
  const [count, setCount] = useCounter(10)

  return (
    <div className="app-container">
      <p>{count}</p>
      <button onClick={setCount}>Increment</button>
    </div>
  )
}

export default App

2. useEffect()

Previously, in class components, we could manage the state lifecycle using componentDidMount and other functions. Now, we can use useEffect to manage component rendering or handle third-party API request data.

useEffect() has two parameters: the first is the callback function, which defines the operation to be executed. The second parameter is the dependencies for the effect. When any of the dependencies change, the useEffect will be triggered.

Effect Dependencies [] can have 0 or multiple parameters.

  • 0 parameters [] is similar to componentDidMount, meaning it triggers when the component first renders.
  • Multiple parameters will trigger the effect whenever any of the dependencies are updated.
useEffect(() => {
  //action
}, [dependencies])

2.1 API Data Fetching

If it's a POST request or if you need to call fetchSubject later, the second parameter can also include those dependencies.

useSubjectApi.js

import { useState, useEffect } from "react"

const useSubjectApi = subject_id => {
  const [subject, setSubject] = useState()
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(false)

  useEffect(() => {
    const fetchSubject = async () => {
      try {
        const response = await fetch(
          `https://api.bgm.tv/subject/${subject_id}?responseGroup=large`
        )
        const data = await response.json()
        setSubject(data)
      } catch (err) {
        setError(true)
      }
      setLoading(false)
    }
    fetchSubject()
  }, [URL])

  const res = {
    subject,
    loading,
    error,
  }

  return [res] 
}

export default useSubjectApi

App.js

import React, { useState, useEffect } from "react"
import useSubjectApi from "./Hooks/useSubjectApi"

const App = () => {
  const [data] = useSubjectApi(51)

  if (data.error || data.loading) {
    return <p>Loading...</p>
  }

  const subject = data.subject
  return (
    <>
      <h1>{subject.name}</h1>
      <img src={subject.images.large} style={{ width: 150 }} />
    </>
  )
}

export default App

3. useContext()

  1. Create the relevant state, naming it something like UserContext, and initialize the state, or set it to null.
  2. Use Provider at the parent level to provide the data where needed.
  3. In the child components that need the state, use useContext to act as a Consumer and receive the data.

UserContext.js

import { createContext } from "react"

export const UserContext = createContext(null)

Using the previously created UserContext as the provider (Provider), all child components can use useContext to access the state provided by the Provider.

App.js

import React, { useState, useEffect } from "react"
import { UserContext } from "./Context/UserContext"
import Dashboard from "./Components/Dashboard"
import ProfileMenu from "./Components/ProfileMenu"

const App = () => {
  return (
    <UserContext.Provider
      value={{
        userName: "yang_tk",
        status: "online",
      }}
    >
      <>
        <ProfileMenu />
        <Dashboard />
      </>
    </UserContext.Provider>
  )
}

export default App

Then, we can reference the state of our UserNameContext in both ProfileMenu and Dashboard components.

ProfileMenu.js

import React, { useContext } from "react"
import { UserContext } from "../Context/UserContext"

const ProfileMenu = () => {
  const user = useContext(UserContext)

  return (
    <>
      <h1>This is profile menu</h1>
      <p>{user.userName}</p>
      <p>{user.status}</p>
    </>
  )
}

export default ProfileMenu

Dashboard.js

import React, { useContext } from "react"
import { UserContext } from "../Context/UserContext"

const Dashboard = () => {
  const { userName } = useContext(UserContext)
  return (
    <>
      <h1>This is dashboard</h1>
      <p>Welcome back {userName}</p>
    </>
  )
}

export default Dashboard

4. useReducer(): 状态管理

Let's first look at the parameters of useReducer():

const [state, dispatch] = useReducer(reducer, initialState)
  • state is the current state value of the function.
  • dispatch is used to dispatch actions to trigger state changes.
  • reducer is the function that defines how the state changes based on the dispatched actions.
  • initialState is the initial value of the state.

counterReducer.js

const counterReducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return {
        ...state,
        count: state.count + 1,
      }
    case "DECREMENT":
      return {
        ...state,
        count: state.count - 1,
      }
    case "RESET":
      return {
        ...state,
        count: 0,
      }
    default:
      return state
  }
}

export default counterReducer

App.js

import { counterReducer } from "./reducers/counterReducer"

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

  return (
    <>
      <button onClick={() => dispatch({ type: "increment" })}>Increment</button>
      <button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
      <p>{state.count}</p>
    </>
  )
}

export default App