React-Hooks

发布时间:2023年12月17日

一、类组件和函数式组件的对比

Hook 的特性:在编写class 的情况下,使用state 以及其他React 特性(比如生命周期)

类组件相比于函数式组件的优势:

  1. 类组件可以定义自己的state,用来保存组件自己内部的状态。函数式组件不可以,因为函数每次调用都会产生新的临时变量
  2. 类组件有自己的生命周期,可以在对应的生命周期中完成自己的逻辑。比如在componentDidMount 中发送网络请求,并且该声明周期函数只会执行一次。函数式组件在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求。
  3. 类组件可以在状态改变时只会重新执行render 函数,以及重新调用的声明周期函数componentDidUpdate 等,函数式组件在重新渲染时,整个函数都会被执行

二、Hooks 的使用

1、useState

  • 在函数退出后函数中定义的变量会“消失”, 而state 中的变量会被React 保留
  • useState 接收唯一一个参数,在第一次组件被调用时使用并作为初始值(如果没有传递参数,那么初始值为undefined)
  • useState 的返回值是一个数组,可以通过解构获取state 和setState
import { memo, useState } from "react"
function CounterHook(props) {
    const [ counter, setCounter ] = useState(0)
    function decrement() {
        setCounter(counter - 1)
    }
    return (
        <div>   
            <h2>当前计数: {counter}</h2>
            <button onClick={e=>setCount(count+1)}>+1</button>
            <button onClick={decrement}>-1</button>
        </div>
    )
}
export default memo(CounterHook)

2、useEffect

a、useEffect 的基本使用
  • useEffect 传入的回调函数会在组件被渲染完成后,会自动执行
  • effect 在每次渲染的时候都会执行
// 在组件第一次渲染、组件更新时都会执行
useEffect(() => {
    // 当前传入的回调函数会在组件被渲染完成后,会自动执行
    // 网络请求/DOM操作/事件监听
})
b、useEffect 传入的回调函数的返回值 (清除操作)
  • useEffect 传入的回调函数可以有一个返回值,这个返回值是另外一个回调函数。这是effect 可选的清除机制,每个effect 都可以返回一个清除函数,这样就可以将添加和移除订阅的逻辑放在一起
  • react 会在组件更新和卸载的时候执行清除操作
useEffect(() => {
    console.log("")
    // 回调函数:回调函数 => 组件被重新渲染或者组件卸载的时候执行
    return () => {
        console.log("")
    }
})
?c、一个函数式组件可以有多个useEffect
useEffect(() => {
    console.log("修改state")
})
useEffect(() => {
    console.log("监听redux 中的数据")
    return () => {
        // 取消redux 中数据的监听
    }
})
d、useEffect 的两个参数(effect 的性能优化)
  1. 参数一:执行的回调函数
  2. 参数二:该useEffect 在哪些state 发生变化时,才重新执行(受谁的影响)
e、useEffect 的第二个参数是空数组时
  • 当useEffect 的第二个参数是空数组时,只在组件第一次渲染时执行
  • 类似于类组件的 componentDidMount
  • 如果useEffect 的第一个参数回调函数有返回值时,只会在组件卸载时执行
  • 类似于类组件的componentWillUnmount
useEffect(() => {
    console.log("在组件第一次渲染时执行一次")
    return () => {
        console.log("在组件卸载时执行一次")
    }
}, [])
f、useEffect 的第二个参数不是空数组时
  • useEffect 的第二个参数是数组,并且数组中有依赖的值
  • useEffect 会在组件第一次渲染时执行一次
  • 并且当依赖的值发生变化时,useEffect 会自动执行
  • 该useEffect 在哪些state 发生变化时,才重新执行(受谁的影响)
useEffect(() => {
    console.log("count 发生了变化")
}, [count])

3、useContext

组件中使用共享的Context 有两种方式:

  1. 类组件可以通过类名contextType = MyContext 方式,在类中获取context
  2. 多个Context 或者在函数式组件中通过MyContext.Consumer 方式共享context

注意事项:

  • 当组件上层最近的<MyContext.Provider> 更新时,该Hook 会触发重新渲染,并使用最新传递给MyContext provider 的context value 值
// src/context/index.js
import { createContext } from "react"
const UserContext = createContext()
const ThemeContext = createContext()
export {
    UserContext,
    ThemeCoontext
}

// src/index.js
import { UserContext, ThemeContext } from "@/context/index.js"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <UserContext.Provider value={{ name: "why", age: 18 }}>
        <ThemeContext.Provider value={{ color: "red", size: 30 }}>
            <App/>
        </ThemeContext.Provider>
    </UserContext.Provider>
);

// src/App.jsx
import { memo, useContext } from 'react'
import { UserContext, ThemeContext } from "@/context/index.js"
const App = memo(() => {
    // 使用context
    const user = useContext(UserContext)
    const theme = useContext(ThemeContext)
    return (
        <div>
            <h2>{ user.name } - { user.age }</h2>
            <div style={{ color: theme.color, fontSize: theme.size }}>主题</div>
        </div> 
    )
})
export default App

4、useReducer(了解)

  • useReducer 是useState 的一种替代方案
  • 在某些情况下,如果state 的处理逻辑比较复杂时,可以通过useReducer 来对其进行拆分
  • 或者这次修改的state 需要依赖之前的state 时,也可以使用
import { useReducer } from 'react'
function reducer(state, action) {
    switch(action.type) {
        case "increment"
            return { ...state, counter: state.counter + 1 }
        case "decrement"
            return { ...state, counter: state.counter - 1 }
        case "add_number"
            return { ...state, counter: state.counter + action.num }
        case "sub_number"
            return { ...state, counter: state.counter - action.num }
        
    }
}
const App = memo(() => {
    const [state,dispatch] = useReducer(reducer, {counter: 0})
    return (
        <div>
            <h2>{state.counter}</h2>
            <button onClick={e => dispatch({ type: "increment" })}>+1</button>
            <button onClick={e => dispatch({ type: "decrement" })}>-1</button>
            <button onClick={e => dispatch({ type: "add_number", num: 5 })}>+5</button>
            <button onClick={e => dispatch({ type: "sub_number", num: 5 })}>-5</button>
        </div>
    )
})

5、useCallback(性能优化)

  • useCallback 性能优化:当需要将一个函数传递给子组件时,最好使用useCallback 进行优化,将优化之后的函数传递给子组件
  • 在依赖不变的情况下,多次定义的时候,返回的值是相同的

当修改count 时,会影响子组件HYHome 重新渲染,当修改message 时,子组件不重新渲染

import { useCallback, useRef } from 'react'
const HYHome = memo(function(props) {
    // props 中的属性发生改变时,组件本身就会被重新渲染
    const { increment } = props
    console.log("HYHome 被渲染")
    return (
        <div>
            <button onClick={increment}>increment + 1</button>
        </div>
    ) 
})
const App = memo(function() {
    const [count, setCount] = useState(0)
    const [message, setMessage] = useState("hello")
    // 1. 闭包陷阱
    const increment = useCallback(function foo() {
        console.log("increment")
        setCount(count + 1)
    }, [count])
    // 2. 使用useRef 实现
    const countRef = useRef()
    countRef.current = count
    const increment = useCallback(function foo() {
        console.log("inncrement")
        setCount(countRef.current + 1)
    }, [])
    // 3. 普通的函数
    const increment = () => {
        setCount(count +1)
    }
    return (
        <div>
            <h2>计数: {count}</h2>
            <button onClick={increment}>+1</button>
            <HYHome increment={increment}/>
            <h2>{ message }</h2>
            <button onCick={e => setMessage(Math.random())}>修改message</button>
        </div>
    )
})    

6、useMemo

文章来源:https://blog.csdn.net/qq_50829019/article/details/134978132
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。