Getting Started with React18 (Part 3) - Detailed explanation of React Hooks, use of React built-in Hooks and custom Hooks

Overview

React Hooks can be said to be one of the most important contents of React . Common React Hooks names usually start with use, such as useState, useEffect, etc. This article will use detailed explanations with pictures and texts to help you quickly understand: the use of React built-in Hooks, custom Hooks (reused code), and third-party Hooks.


1. Built-in Hook——useState

1.1 Responsive data update
import React, {
    
     useState } from 'react'

function App() {
    
    
  // let count = 0  // 普通的 js 变量,无法触发组件的更新
  const [count, setCount] = useState(0) // useState 可以触发组件的更新

  function add() {
    
    
    setCount(count + 1)
    console.log(count, 'count')
  }
  return (
    <>
      <div>
        <button onClick={
    
    add}>add {
    
    count}</button>
      </div>
    </>
  )
}
export default App


1.2 What is state
  • props is the information passed by the parent component
  • state is the internal status information of the component, not external
  • State changes trigger component updates and rerender pages.
1.3 state features (1) - asynchronous update
  • Write a simple accumulation method, and the printed count will always be the count before accumulation. Not a synchronous update
import React, {
    
     FC, useState } from 'react'

const StateDemo: FC = () => {
    
    
  const [count, setCount] = useState(0) // useState 可以触发组件的更新
  const [name, setName] = useState('张三')

  function add() {
    
    
    // 写法一:
    setCount(count + 1)
    // 写法二:
    // setCount(count => count + 1)
    
    /** 打印出来的 count 永远是累加之前的 count */
    console.log(count, 'cur count') // 异步更新,无法直接拿到最新的 state 值 
  }

  return (
    <>
       <button onClick={
    
    add}>add {
    
    count}</button>
    </>
  )
}
export default StateDemo
  • If a variable is not used for display in JSX, then do not use setState to manage it, use useRef
import React, {
    
     FC, useState } from 'react'

const StateDemo: FC = () => {
    
    
  const [name, setName] = useState('张三')

  function add() {
    
    
    setName('李四')
    console.log(name) // 如果说一个变量不用于 JSX 中显示,那就不要用 setState 来管理它,用 useRef
  }
}
export default StateDemo
1.4 state features (2) - may be merged
import React, {
    
     FC, useState } from 'react'

const StateDemo: FC = () => {
    
    
  const [count, setCount] = useState(0) // useState 可以触发组件的更新

  function add() {
    
    
    /** 方法一:看代码觉得可能会实现 count + 5,但由于是异步更新,每次执行的时候,count 任然是0。所以最后仍然是 count + 1 */
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
    
    /** 方法二:可以实现 count + 5 */
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)
    
    console.log(count, 'cur count')
  }

  return (
    <>
      <div>
        <button onClick={
    
    add}>add {
    
    count}</button>
      </div>
    </>
  )
}
export default StateDemo
1.5 State Features (3) - Immutable Data (Important!!!)

注:编辑 state 的数据只能是传入一个的数据进行覆盖,而不能修改原数据

import React, {
    
     FC, useState } from 'react'
import QuestionCard from './components/QuestionCard'

const List2: FC = () => {
    
    
  // 问卷列表数据
  const [questionList, setQuestionList] = useState([
    {
    
     id: 'q1', title: '问卷1', isPublished: false },
    {
    
     id: 'q2', title: '问卷2', isPublished: true },
    {
    
     id: 'q3', title: '问卷3', isPublished: true },
    {
    
     id: 'q4', title: '问卷4', isPublished: false },
  ])

  // 新增问卷
  function handleAdd() {
    
    
    // 生成三位随机数
    const r = Math.random().toString().slice(-3)
    setQuestionList(
      questionList.concat({
    
    
        id: 'q' + r,
        title: '问卷' + r,
        isPublished: false,
      })
    )
  }

  // 删除问卷
  function deleteQuestion(id: string) {
    
    
    setQuestionList(questionList.filter(item => item.id != id))
  }

  // 编辑问卷
  function publishedQuestion(id: string) {
    
    
    setQuestionList(
      questionList.map(item => {
    
    
        if (item.id !== id) return item
        return {
    
    
          ...item,
          isPublished: true,
        }
      })
    )
  }
}
export default List2
1.6 Use immer to modify state
  • state is immutable data
  • state has high operating costs and great instability
  • 使用 immer 可避免这一个问题

Install immer

npm install immer --save

Use immer

import React, {
    
     FC, useState } from 'react'
import {
    
     produce } from 'immer'

const Demo: FC = () => {
    
    
  const [userInfo, setUserInfo] = useState({
    
     name: '张三', age: 24 })

  // 修改个人信息
  const handleChangeUser = () => {
    
    
    setUserInfo(
      // 通过 immer,即可让我们便捷的修改数据
      produce(draft => {
    
    
        draft.name = '李四'
      })
    )
  }
  return (
    <div>
      <div>{
    
    JSON.stringify(userInfo)}</div>
      <button onClick={
    
    handleChangeUser}>change age</button>
    </div>
  )
}
export default Demo

2. Built-in Hook——useEffect

2.1 Function and use
import React, {
    
     FC, useState, useEffect } from 'react'
import {
    
     produce } from 'immer'
import QuestionCard from './components/QuestionCard'

const List2: FC = () => {
    
    
  /** 方式一:直接发起 Ajax 请求。不行,state值改变时会触发请求,造成重复发送冗余请求 */
  // console.log('方式一加载 Ajax 网络请求')

  /** 方式二:此方法即可 */
  useEffect(() => {
    
    
    console.log('方式二加载 Ajax 网络请求')
  }, []) // 第二个参数为依赖项, 数组中可以放多个 state 值,当 state 值改变时,会触发 函数(第一个参数)的执行。若为空,
  const [questionList, setQuestionList] = useState([
    {
    
     id: 'q1', title: '问卷1', isPublished: false },
    {
    
     id: 'q2', title: '问卷2', isPublished: true },
    {
    
     id: 'q3', title: '问卷3', isPublished: true },
    {
    
     id: 'q4', title: '问卷4', isPublished: false },
  ])
}
export default List2
2.2 Execution timing

It will be executed when the component is created, destroyed and the dependent state data changes.

import React, {
    
     FC, useState, useEffect } from 'react'
import {
    
     produce } from 'immer'
import QuestionCard from './components/QuestionCard'

const List2: FC = () => {
    
    
  /** 方式一:直接发起 Ajax 请求。不行,会频繁的重复请求 */
  // console.log('方式一加载 Ajax 网络请求')

  /** 方式二:此方法即可 */
  useEffect(() => {
    
    
    // 组件挂载时执行
    console.log('component mounted')

    return () => {
    
    
      // 组件销毁时执行
      console.log('component unmounted')
    }
  }, [questionList]) // 如果此数组中依赖数据,则依赖数据更新时会执行
  const [questionList, setQuestionList] = useState([
    {
    
     id: 'q1', title: '问卷1', isPublished: false },
    {
    
     id: 'q2', title: '问卷2', isPublished: true },
  ])
}
export default List2
2.3 useEffect is executed twice
  • Starting from React18, useEffect will be executed twice indevelopment environment
  • The purpose is to simulate the complete process of left-click creation, destruction, and re-creation, and expose problems as early as possible.
  • Production environmentWill be executed once

3. Other built-in Hooks

3.1 useRef - used for Dom nodes and JS variables
  • Generally used to manipulate DOM
  • Ordinary JS variables can also be passed in, but updates will not trigger rerender
  • To be distinguished from Vue3 ref (if you have used Vue3)

Sample code one: Manipulating DOM

import React, {
    
     FC, useRef } from 'react'

const UseRefDemo: FC = () => {
    
    
  const inputRef = useRef<HTMLInputElement>(null)

  function handelSelect() {
    
    
    const element = inputRef.current  // 可以拿到 DOM 节点,进行 DOM 操作
    element?.select()
  }
  return (
    <div>
      <input ref={
    
    inputRef} defaultValue="Hello Word" />
      <button onClick={
    
    handelSelect}>select button</button>
    </div>
  )
}
export default UseRefDemo

Sample code 2: rerender will not be triggered

import React, {
    
     FC, useRef } from 'react'

const UseRefDemo: FC = () => {
    
    
  const name = useRef('张三')

  function handleChangeName() {
    
    
    name.current = '王小虎' // 修改 ref 的值,不会触发 rerender(修改 state 的值,会触发 rerender)
    console.log(name.current, 'now name') // 此时页面仍然是 张三,但是此处打印出来的是 王小虎
  }
  return (
    <div>
      <div>{
    
    name.current}</div>
      <button onClick={
    
    handleChangeName}>select button</button>
    </div>
  )
}
export default UseRefDemo
3.2 useMemo - cache variables
  • Function component, the function will be re-executed every time the state is updated
  • useMemo cancache data without regenerating it every time the function is executed
  • Can be used in scenarios with a large amount of calculation, caching improves performance

Using sample code:

import React, {
    
     FC, useMemo, useState } from 'react'

const Demo: FC = () => {
    
    
  const [num1, setNum1] = useState(10)
  const [num2, setNum2] = useState(20)
  const [test, setTest] = useState('测试')  // 更新组件,导致组件 rerender

  const sum = useMemo(() => {
    
    
    return num1 + num2
  }, [num1, num2])
  return (
    <>
      <p> {
    
    sum} </p>
      <p> {
    
    num1}{
    
    ' '}<button onClick={
    
    () => {
    
     setNum1(num1 + 1) }}>add num1</button></p>
      
      <p> {
    
    num2}{
    
    ' '} <button onClick={
    
    () => {
    
    setNum2(num2 + 2)}}>add num2</button></p>
      <div>
        {
    
    /* form组件,受控组件 */}
        <input value={
    
    test} onChange={
    
    e => setTest(e.target.value)} />
      </div>
    </>
  )
}
export default Demo
3.3 useCallback—— cache function
  • Similar to useMemo
  • Specifically for **cache functions**
  • The usage depends on the business and cannot be optimized for the sake of optimization.
import React, {
    
     FC, useState, useCallback } from 'react'

const Demo: FC = () => {
    
    
  const [text, setText] = useState('Hello')

  // fn1 是只要组件重新更新、重新执行,那么 fn1 就会被重新定义 -- 无缓存
  const fn1 = () => console.log('执行 fn1 ')

  // fn2 用了 useCallback ,就会根据依赖项去重新定义 -- 有缓存
  const fn2 = useCallback(() => {
    
    
    console.log('执行 fn2')
  }, [text]) // 依赖项,与 useEffect,useMemo,useCallback 用法类似

  // 缓存,为了性能优化,提升时间效率
  // 但是不能为了优化,要根据业务而定

  return (
    <>
      <div>
        <button onClick={
    
    fn1}>fn 1</button> &nbsp; <button onClick={
    
    fn2}>fn 2</button>
      </div>
      {
    
    /* form组件,受控组件 */}
      <input value={
    
    text} onChange={
    
    e => setText(e.target.value)} />
    </>
  )
}
export default Demo

4. Custom Hook

4.1 Correct way to open React Hooks
  • Built-in Hooks ensure basic functions
  • Built-in Hooks can be used flexibly to implement business functions
  • Separate the functional part, customize Hooks or third-party Hooks - reuse hooks
4.2 Extraction and reuse of common logic of React components
  • It used to be a class component, now it is a function component
  • Class components: Mixin (mixed), HOC (high-order component), render-props (rendering properties) to reuse common logic
  • Function component: using Hooks - currently最完美的解决方案, Vue3 is also referenced
4.3 Custom Hook - Modify web page title

4.3.1 Define a hooks

// /src/hooks/useTitle.ts
import {
    
     useEffect } from 'react'

function useTitle(title: string) {
    
    
  useEffect(() => {
    
    
    // 修改网页标题
    document.title = title
  }, [])
}
export default useTitle

4.3.2 Using custom hooks

import React from 'react'
// 引入 自定的 hooks
import useTitle from './hooks/useTitle'

function App() {
    
    
  ... 其他代码
  
  // 使用自定义的 hooks
  useTitle('React 自定义 Hook 学习')
 
 ... 其他代码
}
export default App

4.3.3 Page effects
Insert image description here


5. Third-party Hooks

5.1 Common third-party Hooks
5.2 Use of ahooks

Install ahooks

npm install --save ahooks

use

import React from 'react';
import {
    
     useTitle } from 'ahooks';

export default () => {
    
    
  useTitle('Page Title');

  return (
    <div>
      <p>Set title of the page.</p>
    </div>
  );
};

6. Hooks usage rules

  • Must be named in useXxx format
  • Hook can only be called in two places (*Inside components, in other Hooks *)
  • The order of each call must be consistent (Hook cannot be placed inside if for)

Guess you like

Origin blog.csdn.net/qq_61402485/article/details/133542470