56 lines
1.5 KiB
Markdown
56 lines
1.5 KiB
Markdown
---
|
||
title: Store Event Handlers in Refs
|
||
impact: LOW
|
||
impactDescription: 稳定的订阅
|
||
tags: advanced, hooks, refs, event-handlers, optimization
|
||
---
|
||
|
||
## 将事件处理函数存储在 Refs 中
|
||
|
||
当回调函数被用于那些不应因回调变化而重新订阅的 effect 时,应将回调存储在 ref 中。
|
||
|
||
**错误做法(每次渲染都重新订阅):**
|
||
|
||
```tsx
|
||
function useWindowEvent(event: string, handler: (e) => void) {
|
||
useEffect(() => {
|
||
window.addEventListener(event, handler)
|
||
return () => window.removeEventListener(event, handler)
|
||
}, [event, handler])
|
||
}
|
||
```
|
||
|
||
**正确做法(稳定的订阅):**
|
||
|
||
```tsx
|
||
function useWindowEvent(event: string, handler: (e) => void) {
|
||
const handlerRef = useRef(handler)
|
||
useEffect(() => {
|
||
handlerRef.current = handler
|
||
}, [handler])
|
||
|
||
useEffect(() => {
|
||
const listener = (e) => handlerRef.current(e)
|
||
window.addEventListener(event, listener)
|
||
return () => window.removeEventListener(event, listener)
|
||
}, [event])
|
||
}
|
||
```
|
||
|
||
**替代方案:如果你在使用最新版 React,可以使用 `useEffectEvent`:**
|
||
|
||
```tsx
|
||
import { useEffectEvent } from 'react'
|
||
|
||
function useWindowEvent(event: string, handler: (e) => void) {
|
||
const onEvent = useEffectEvent(handler)
|
||
|
||
useEffect(() => {
|
||
window.addEventListener(event, onEvent)
|
||
return () => window.removeEventListener(event, onEvent)
|
||
}, [event])
|
||
}
|
||
```
|
||
|
||
`useEffectEvent` 为同一模式提供了更简洁的 API:它创建一个稳定的函数引用,始终调用最新版本的处理函数。
|