React Hooks UseCallback

发布于:2025-09-06 ⋅ 阅读:(17) ⋅ 点赞:(0)

  开发环境:React Native + Taro + Typescript
    useCallback的用途,主要用于性能优化:
        1 避免不必要的子组件重渲染:当父组件重渲染时,如果传递给子组件的函数每次都是新创建的,即使子组件使用了 React.memo,也会导致子组件重新渲染。
        2 作为其他 Hook 的依赖项:当函数被用作 useEffect、useMemo 或其他 Hook 的依赖项时,使用 useCallback 可以确保函数引用稳定,避免不必要的 effect 执行。
        3 记忆化函数:对于创建成本较高的函数,使用 useCallback 可以避免在每次渲染时都重新创建。

    useCallback的优势:
        1 性能优化:通过缓存函数实例,减少不必要的重渲染和函数创建,提高应用性能。
        2 避免无限循环:在 useEffect 中使用函数时,如果函数没有使用 useCallback 包装,可能会导致无限重渲染。
        3 稳定的函数引用:确保在依赖项不变的情况下,函数引用保持不变,这对于优化子组件渲染特别有用。

    使用时的注意事项:
        1 不要过度使用:只有在确实需要优化性能时才使用 useCallback,因为useCallback本身也有性能开销。
        2 正确设置依赖数组:确保依赖数组中包含所有在函数内部使用且可能变化的值,否则可能导致闭包问题。
        3 与 React.memo 配合使用:useCallback 通常与 React.memo 一起使用才能发挥最大效果。

        下面是父组件:UseCallback

import React, { useState, useCallback, useEffect } from 'react'
import { View, Text, Button, ScrollView } from '@tarojs/components'
import styles from './plant.module.scss'
import Strawberry from './UseCallbackSub'


 const PlantManager: React.FC = ()=>{

    useEffect(()=>{
        console.log("父组件:useEffect, 组件重渲染!!!")
    })
    

    // 草本植物科目数量 初始化
    const [subjectNum, setSubjectNum] = useState(0)

    // 蔷薇科的植物或水果的颜色 初始化
    const [rosaceaeColor, setrosaceaeColor] = useState('green')

    // 蔷薇科的植物或水果size大小 初始化
    const [rosaceaeSize, setRosaceaeSize] = useState(5)

    // 蔷薇科植物或水果的颜色 改变次数,需要改变草莓颜色时,需要在此加一
    const [changeColorNum, setChangeColorNum] = useState(0)

    // 没有使用useCallback的函数,每次渲染都会创建新的函数实例
    const handleInrement = () => {
        console.log('父组件 handleInrement')
        // setSubjectNum(preSubjectNum => preSubjectNum + 1);
    }

    // 使用useCallback缓存函数,同样改变subjectNum的值
    const handleUseCallbackChangeSubjectNum = useCallback(()=>{
        console.log('父组件 handleUseCallbackChangeSubjectNum')
        // setSubjectNum(preSubjectNum => preSubjectNum + 1);
    }, []); //这里的依赖数组为空,是因为setSubjectNum是稳定的

    // 使用useCallback缓存函数,并带有依赖项
    // 改变子组件中草莓的颜色,并查看子组件渲染的时机与次数
    const handleChangeRosaceaeColor = useCallback(()=>{
        const color = rosaceaeColor === 'green' ? 'green' : 'red'
        setrosaceaeColor(color)
    }, [changeColorNum])
    const handleChangeColorNum = useCallback(()=>{
        setChangeColorNum(preNum => preNum + 1)
    }, [])

    // 使用useCallback缓存函数,没有依赖项
    // 改变子组件中草莓的size大小,并查看子组件渲染的时机与次数
    const handleChangeRosaceaeSize = useCallback(()=>{
        setRosaceaeSize(preSize => preSize + 1)
    }, [])

    return (
        <ScrollView
            scrollY
            scrollWithAnimation
            className={styles.scroll}
            style={{
                height: '40vh',
            }}
        >
        <View className={styles.container}>
            <View className={styles.showTextBox}>
                <Text>草本植物的数量 subjectNum = {subjectNum}</Text>
                <Text>蔷薇科植物颜色需要改变的次数 = {changeColorNum}</Text>
                <Strawberry 
                    color={rosaceaeColor}
                    increment={handleInrement}
                    changeSubjectNum={handleUseCallbackChangeSubjectNum}
                    size={rosaceaeSize}>
                </Strawberry>
            </View>
            <View className={styles.showTextBox}>
                <Text className={styles.showText}>使用useCallback带有依赖项,改变子组件中草莓的颜色</Text>
                <Button onClick={handleChangeRosaceaeColor} className={styles.actionBox} >改变子组件的草莓颜色</Button>
                <Button onClick={handleChangeColorNum} className={styles.actionBox}>需要改变蔷薇科植物颜色时,给changeColorNum加一</Button>
            </View>
            <View className={styles.showTextBox}>
                <Text className={styles.showText}>使用useCallback没有带依赖项,改变子组件中草莓size大小</Text>
                <Button className={styles.actionBox} onClick={handleChangeRosaceaeSize}>改变子组件草莓的size</Button>
            </View>
        </View>
        </ScrollView>
    )
}

export default PlantManager

        下面是子组件:UseCallbackSub

import React, { useEffect, memo } from 'react'
import { View, Text, Button } from '@tarojs/components'
import styles from './plant.module.scss'

interface StrawberryProps {
    color: string
    size: number
    increment: ()=>void
    changeSubjectNum: ()=>void
}

// 定义一个子组件,用于查看子组件的更新状态
 const Strawberry: React.FC<StrawberryProps> = (props)=>{

    const { increment, changeSubjectNum } = props;

    useEffect(()=>{
        console.log("子组件重新渲染更新!!!")
        console.log("当前草莓的颜色: ", props.color);
    })

    return (
        <View className={styles.berryBox}>
            <Text className={styles.showText}>子组件 草莓</Text>
            <Text className={styles.berryTxt} style={{
                backgroundColor: props.color,
            }}>
                草莓的颜色
            </Text>
            <Text className={styles.berryTxt}>草莓的大小size = {props.size}</Text>
            <View className={styles.showTextBox}>
                <Text className={styles.showText}>普通函数,没有使用useCallback缓存, 每次都会重新创建</Text>
                <Button className={styles.actionBox} onClick={increment}>增加草本植物的数量</Button>
            </View>
            <View className={styles.showTextBox}>
                <Text className={styles.showText}>使用useCallback缓存函数</Text>
                <Button className={styles.actionBox} onClick={changeSubjectNum}>增加草本植物的数量</Button>
            </View>
        </View>
    )
 }

// export default Strawberry;
export default memo(Strawberry);

        下面的文件是样式:

.container {
    margin-top: 30px;
    margin-bottom: 50px;
    margin-left: 15px;
    margin-right: 15px;
    padding: 20px;
    background-color: darkgrey;
}

.scroll {
    margin-top: 30px;
    margin-bottom: 30px;
    margin-left: 15px;
    margin-right: 15px;
}

.showTextBox {
    background-color: antiquewhite;
    padding: 10px;
    text-align: center;
    margin-bottom: 30px;
}

.showText {
    padding: 20px;
    text-align: center;
    margin-top: 18px;
    background-color: cornflowerblue;
}

.actionBox {
    background-color: aquamarine;
    padding: 20px;
    margin-top: 20px;
}

.actionContainer {
    padding: 15px;
    margin-top: 30px;
}

.berryBox {
    background-color: darksalmon;
    padding: 30px;
}

.berryTxt {
    padding-left: 80px;
    padding-right: 80px;
    padding-top: 30px;
    padding-bottom: 30px;
    margin-bottom: 35px;
}


网站公告

今日签到

点亮在社区的每一天
去签到