一、先說一下react
react是基於數據是不可變的(每次setState都會返回一個新數據),這也是為什么需要setState()來更新數據而不能使用像vue的this.state = newState的形式更新數據的原因,其實你用this.state=newState確實可以改數據,但是react不知道數據變了。
二、useMemo、useEffect的執行時機對比
useMemo和useCallback都會在組件第一次渲染的時候執行,之后會在其依賴的變量發生改變時再次執行;並且這兩個hooks都返回緩存的值,useMemo返回緩存的變量,useCallback返回緩存的函數。
三、useMemo的使用場景
使用過vue的話,你可以把它理解成vue里面的computed,是一種數據的緩存,而這個緩存依賴后面的第二個參數數組,如果這個數組里面傳入的數據不變,那么這個useMemo返回的數據是之前里面return的數據。
在具體項目中,如果你的頁面上展示的數據是通過某個(某些)state計算得來的一個數據,那么你每次這個組件里面無關的state變化引起的重新渲染,都會去計算一下這個數據,這時候就需要用useMemo(()=>{}, [])去包裹你的計算的方法體,這樣那些無關的state改變引起的渲染不會重新計算這個方法體,而是返回之前計算的結果,達到一種緩存的效果。
export default function WithMemo() { const [count, setCount] = useState(1); const [val, setValue] = useState(''); const expensive = useMemo(() => { console.log('compute'); let sum = 0; for (let i = 0; i < count * 100; i++) { sum += i; } return sum; }, [count]); return <div> <h4>{count}-{expensive}</h4> {val} <div> <button onClick={() => setCount(count + 1)}>+c1</button> <input value={val} onChange={event => setValue(event.target.value)}/> </div> </div>; }
四、useCallback使用場景
useCallback跟useMemo比較類似,但它返回的是緩存的函數。
hooks組件state改變后會引起父組件的重新渲染,而每次重新渲染都會生成一個新函數,所以react子組件props在淺比較的時候就會認為props改變了,引起子組件不必要的渲染。
使用場景是:有一個父組件,其中包含子組件,子組件接收一個函數作為props;通常而言,如果父組件更新了,子組件也會執行更新;但是大多數場景下,更新是沒有必要的,我們可以借助useCallback來返回函數,然后把這個函數作為props傳遞給子組件;這樣,子組件就能避免不必要的更新。
為什么useCallback需要配合React.memo來使用?
react的Hooks組件對props的淺比較是在memo里面比較的(類組件是在shouldComponentUpdate里面),如果沒有memo,那么你使用useCallback就沒啥意義,反而浪費性能(因為useCallback來包裹函數也是需要開銷的)。因為子組件還是會重新渲染。
function APP() { const [value, setValue] = useState(123) const [otherValue, setOtherValue] = useState(999) const changeValue = useCallback(() => { setValue(value => value+1) }, []) console.log('APP'); return ( <div> <div>與Message渲染無關的數據==={otherValue}</div> <br /> <button onClick={() => setOtherValue(value => value-=5)}>改變無關的數據</button> <br /> <br /> <Message value={value} changeValue={changeValue} /> </div> ) } const Message = memo( function Message({value, changeValue}) { console.log('Message'); return ( <div> <button onClick={changeValue}>改變有關數據</button> <p>與Message渲染有關的數據{value}</p> </div> ) } )