React Hooks API

React 最常用 Hooks 介绍: useState, useEffect, useContext, useReducer

React

React Hooks API 演示

目录

1. useState() 管理组件状态

useState()有两个参数,第一个为该 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 表单逻辑 Hooks Demo

例如上面的表单authForm, 可以单独封装为一个表单逻辑 Hooks 专门处理表单输入.

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

因为我们在useForm中返回的是一个数组, 因此命名不需要与useForm中的匹配, 我们是用下标取相对应的参数. (下标 0 第一个就是 state, 下标 1 为管理 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() 管理生命周期

之前在 class 组件中可以利用componentDidMount以及其他函数来管理 state 的生命周期。现在可以利用 useEffect 来管理组件的渲染或者处理第三方 API 的请求数据。

useEffect()中有两个参数,第一个回调函数即要执行的操作。第二个参数为 effect 触发的依赖,当依赖发生变化时就会执行 useEffect。

Effect Dependencies []可有0 个或者多个参数.

0 个参数 [] 类似于componentDidMount即组件第一次渲染时触发.

多个参数则只要任意一个依赖更新了则会触发 effect

useEffect(() => {
  //action
}, [dependencies])

2.1 API Data Fetching 封装 Hooks Demo

如果是POST或者之后还需要fetchSubject, 返回的第二个参数也可以填入.

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] // 这里只用来GET 所以只返回数据
}

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. 创建相关状态, 命名例如 UserContext, 可初始化状态或者初始化为null
  2. 在需要的父级层面用Provider提供数据
  3. 在需要状态的子级组件用useContext来作为Consumer接受数据

UserContext.js

import { createContext } from "react"

export const UserContext = createContext(null)

以之前创建的UserContext作为提供者(Provider), 所有的子级组件都可以用useContext来获取 Provider 的 state

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

然后我们就可以在 ProfileMenu 和 Dashboard 中引用我们UserNameContext的 state

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(): 状态管理

首先来看一下 useReducer()的参数

const [state, dispatch] = useReducer(reducer, initialState)
  • state 即函数的状态值
  • dispath 则是用来打包相关的行为
  • reducer 即我们我们之后 Reducer 的函数
  • initialState 为状态的初始值

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