React Hooks UseCallback
开发环境: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 (<ScrollViewscrollYscrollWithAnimationclassName={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: stringsize: numberincrement: ()=>voidchangeSubjectNum: ()=>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;
}