useRef:
语法: const refObject = useRef(initialValue);
它只是返回一个普通的 JavaScript object。可以根据需要多次访问和修改其值(mutability),而无需担心“重新渲染”。
它的值将persist(不会被重置为initialValue,这与函数组件中定义的普通*对象不同;它会持续存在,因为useRef为您提供了相同的对象,而不是创建在后续渲染中添加一个新的)用于组件生命周期。
如果您在控制台上写入const refObject = useRef(0) 并打印refObject,您将看到日志对象-{ current: 0 }。
*普通对象 vs refObject,示例:
function App() {
const ordinaryObject = { current: 0 } // It will reset to {current:0} at each render
const refObject = useRef(0) // It will persist (won't reset to the initial value) for the component lifetime
return <>...</>
}
几个常见的用法,例子:
- 访问DOM:
<div ref={myRef} />
- 存储可变值,例如instance variable (in class)
- 渲染counter
- 在
setTimeout / setInterval 中使用的值,没有stale closure 问题。
useMemo:
语法:const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
它返回一个memoized 值。这个钩子的主要目的是“性能优化”。在需要时谨慎使用它来优化性能。
它接受两个参数——“create”函数(它应该返回一个要记忆的值)和“dependency”数组。只有当其中一个依赖项发生变化时,它才会重新计算记忆值。
几个常见的用法,例子:
- 在渲染时优化昂贵的计算(例如对数据的操作,如排序、过滤、更改格式等)
未记忆的例子:
function App() {
const [data, setData] = useState([.....])
function format() {
console.log('formatting ...') // this will print at every render
const formattedData = []
data.forEach(item => {
const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
if (newItem) {
formattedData.push(newItem)
}
})
return formattedData
}
const formattedData = format()
return <>
{formattedData.map(item => <div key={item.id}>
{item.title}
</div>)}
</>
}
记忆示例:
function App() {
const [data, setData] = useState([.....])
function format() {
console.log('formatting ...') // this will print only when data has changed
const formattedData = []
data.forEach(item => {
const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
if (newItem) {
formattedData.push(newItem)
}
})
return formattedData
}
const formattedData = useMemo(format, [data])
return <>
{formattedData.map(item => <div key={item.id}>
{item.title}
</div>)}
<>
}
useCallback:
语法:const memoizedCallback = useCallback(() => { //.. do something with a & b }, [a, b])
它返回一个memoized 函数(或回调)。
它接受两个参数——“函数”和“依赖”数组。只有当其中一个依赖项发生变化时,它才会返回新的,即重新创建的函数,否则它将返回旧的,即记忆的函数。
几个常见的用法,例子:
- 将 memoized 函数传递给子组件(通过
React.memo 或 shouldComponentUpdate 使用浅相等 - Object.is 进行优化)以避免由于作为 props 传递的函数而不必要地重新渲染子组件。
示例 1,没有 useCallback:
const Child = React.memo(function Child({foo}) {
console.log('child rendering ...') // Child will rerender (because foo will be new) whenever MyApp rerenders
return <>Child<>
})
function MyApp() {
function foo() {
// do something
}
return <Child foo={foo}/>
}
示例 1,useCallback:
const Child = React.memo(function Child({foo}) {
console.log('child rendering ...') // Child will NOT rerender whenever MyApp rerenders
// But will rerender only when memoizedFoo is new (and that will happen only when useCallback's dependency would change)
return <>Child<>
})
function MyApp() {
function foo() {
// do something
}
const memoizedFoo = useCallback(foo, [])
return <Child foo={memoizedFoo}/>
}
- 将记忆函数作为依赖项传递给其他钩子。
示例 2,没有 useCallback,不好(但 eslint-plugin-react-hook 会警告您纠正它):
function MyApp() {
function foo() {
// do something with state or props data
}
useEffect(() => {
// do something with foo
// maybe fetch from API and then pass data to foo
foo()
}, [foo])
return <>...<>
}
示例 2,useCallback,好:
function MyApp() {
const memoizedFoo = useCallback(function foo() {
// do something with state or props data
}, [ /* related state / props */])
useEffect(() => {
// do something with memoizedFoo
// maybe fetch from API and then pass data to memoizedFoo
memoizedFoo()
}, [memoizedFoo])
return <>...<>
}
这些钩子规则或实现将来可能会发生变化。所以,请务必检查文档中的hooks reference。此外,请务必注意关于依赖关系的 eslint-plugin-react-hook 警告。如果省略这些钩子的任何依赖,它将指导您。