赞
踩
useMemo
的行为类似 Vue 中的计算属性,可以监测某个数据的变化,根据变化值计算新值,计算出的新值可以参与视图渲染。
useMemo
会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为有助于避免在每个渲染上进行昂贵的计算。
useMemo
接收一个计算函数和依赖项数组。
useMemo
返回的值就是计算函数返回的值。
import { useState, useMemo } from 'react' function App() { const [count, setCount] = useState(0) const result = useMemo(() => { console.log('测试在修改 bool 时是否会重新计算') return count * 2 }, [count]) const [bool, setBool] = useState(true) return ( <div> <span>{result}</span> <span>{count}</span> <button onClick={() => setCount(count+1)}>+1</button> <br/> <span>{bool ? '真' : '假'}</span> <button onClick={() => setBool(!bool)}>改变Bool</button> </div> ); } export default App;
memo
方法是用于性能优化的高阶组件。
如果组件的 props 没有发生变化,可以阻止组件重新渲染,并直接复用最近一次渲染的结果。
import React, { memo, useState } from 'react' /* // count 每次变化都会渲染 Foo 组件 function Foo() { console.log('Foo 组件重新渲染了') return ( <div>Foo 组件</div> ) } */ // count 变化不会重新渲染 Foo 组件 const Foo = memo(function() { console.log('Foo 组件重新渲染了') return ( <div>Foo 组件</div> ) }) function App() { const [count, setCount] = useState(0) return ( <div> <span>{count}</span> <button onClick={() => setCount(count+1)}>+1</button> <Foo /> </div> ); } export default App;
useCallback
也是用于性能优化,它可以缓存函数,使组件重新渲染时得到相同的函数实例。
import { useState, memo } from 'react' const Foo = memo(function(props) { console.log('Foo 组件重新渲染了') return ( <div> <button onClick={() => props.resetCount()}>重置Count</button> </div> ) }) function App() { const [count, setCount] = useState(0) const resetCount = () => { setCount(0) } return <div> <span>{count}</span> <button onClick={() => setCount(count+1)}>+ 1</button> <Foo resetCount={resetCount} /> </div> } export default App
App 组件在 count 更新时就会重新渲染(App 函数就会重新执行),resetCount
也会重新定义,与之前传递给 Foo 的函数实例不一样,所以 Foo 就会重新下渲染。
结果就是,Foo 组件总会在未使用到的 count 的值变化时重新渲染。
为了避免这种不必要的重复渲染,可以使用 useCallback
将 resetCount
方法缓存下来,在 App 重新渲染时,获取缓存中的 resetCount
方法传递给 Foo 组件,由于缓存中的 resetCount
(引用地址)未发生变化,所以 Foo 组件不会被额外渲染。
useCallback
同样可以接受一个依赖项数组作为第二个参数:
const resetCount = useCallback(() => {
setCount(0)
}, [])
或
const resetCount = useCallback(() => {
setCount(0)
}, [setCount])
useRef
有两个功能:
useRef(initial)
会返回一个可变的 ref 对象。该对象只有一个 current 属性,初始值时 initial。
当把 ref 对象传递给组件或元素的 ref 属性后,ref 对象的 current 就指向该 DOM 元素对象。
节点变化时只会改变 ref 对象的 current 属性,不会触发组件重新渲染。
函数型组件使用 useRef
:
import { useRef } from 'react'
function App() {
const box = useRef()
return <div>
<button onClick={() => console.log(box)}>获取 DIV</button>
</div>
}
export default App
类组件使用 createRef
:
import React from 'react' class App extends React.Component { constructor(props) { super(props) this.box = React.createRef() } render() { return <div ref={this.box}> <button onClick={() => console.log(this.box)}>获取 DIV</button> </div> } } export default App
useRef
返回的ref 对象在组建的整个生命周期内保持不变,每次重新渲染,都返回同一个 ref 对象。
ref 对象的 current 属性变化,并不会引发组件重新渲染。
本质上,这个 ref 对象就像是可以在其 .current
属性中保存一个可以变值的“盒子”。
所以 useRef
还可以用于保存跨组件周期的数据:
与 useState
保存的数据的区别:useState
保存的是状态数据,当状态发生变化,会触发组件重新渲染。
在副作用函数里创建计时器,定时修改状态,点击按钮停止计时器。
下面的方式不会成功:
import { useState, useEffect } from 'react' function App() { const [count, setCount] = useState(0) // 组件重新渲染 timerId 就会被重置 let timerId = null useEffect(() => { timerId = setInterval(() => { setCount(count => count + 1) }, 1000) }, []) const stopCount = () => { clearInterval(timerId) } return <div> {count} <button onClick={stopCount}>停止</button> </div> } export default App
可以使用 useState
将 timerId 当作状态保存,避免重新渲染时被重置。
import { useState, useEffect } from 'react' function App() { const [count, setCount] = useState(0) // 使用 useState 确保组件重新渲染后 timerId 不会被重置 const [timerId, setTimerId] = useState(null) useEffect(() => { setTimerId(setInterval(() => { setCount(count => count + 1) }, 1000)) }, []) const stopCount = () => { clearInterval(timerId) } return <div> {count} <button onClick={stopCount}>停止</button> </div> } export default App
import { useState, useEffect, useRef } from 'react' function App() { const [count, setCount] = useState(0) // 使用 useRef 保存数据,在整个生命周期都不变 const timerId = useRef(null) useEffect(() => { timerId.current = setInterval(() => { setCount(count => count + 1) }, 1000) }, []) const stopCount = () => { clearInterval(timerId.current) } return <div> {count} <button onClick={stopCount}>停止</button> </div> } export default App
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。