? 開發環境: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;
}