useEventListener
window や document などの任意のターゲットのイベントを、コンポーネントがマウントされている間だけ購読します。
function App() { const [listening, setListening] = useState(true) const [key, setKey] = useState('-') // A function, so that `window` is read in the browser and not while the // page renders on the server. `null` listens on nothing. useEventListener( () => (listening ? window : null), 'keydown', (event) => setKey(event.key === ' ' ? 'Space' : event.key), ) return ( <div style={{ userSelect: 'none' }}> <div style={{ padding: '2rem', marginBottom: '0.5rem', border: '1px solid currentColor', borderRadius: 8, textAlign: 'center', }} > {listening ? 'Press any key' : 'Not listening'} </div> <div>last key: {key}</div> <label> <input type="checkbox" checked={listening} onChange={(event) => setListening(event.target.checked)} />{' '} Listen on window </label> </div> ) }
インポート
import { useEventListener } from '@tremolo-ui/react'
使い方
useEventListener(target, 'keydown', handler, options)
イベント名でハンドラの型が決まります。'keydown' なら KeyboardEvent を受け取ります。
引数はどれもインラインで書けます。 ハンドラはイベントのたびに読み直され、ターゲットとオプションは参照の同一性ではなく、解決した中身で比べます。リスナを付け直すのは、要素・イベント名・オプションのどれかが実際に変わったときだけです。
ターゲット
ターゲットは EventTarget、null、またはそのどちらかを返す関数です。
nullは何も購読しません。しばらく購読をやめたいときに渡します- 関数はレンダーのたびに、ブラウザで呼ばれます。 ref の先の要素を渡すときや、サーバーでもレンダーするページで
window/documentを渡すときに使います。どちらもレンダー中のサーバーには存在しません
const ref = useRef(null)
useEventListener(() => ref.current, 'pointerenter', handler)
useEventListener(() => window, 'resize', handler)
既定のターゲットはありません。document や window も名前で渡してください。
オプション
第 4 引数は addEventListener のオプションです。capture を表す真偽値か、capture / once / passive を持つオブジェクトを渡します。リスナを外すタイミングは hook が決めるので、signal は受け付けません。
useEventListener(() => window, 'touchmove', handler, { passive: false })
Svelte と Vue
この hook は React だけのものです。どちらのフレームワークにも同じことをする手段が既にあるので、@tremolo-ui/svelte と @tremolo-ui/vue には置いていません。
- Svelte
- Vue
<svelte:window> と <svelte:document> は、コンポーネントがマウントされている間だけ購読します。
<svelte:window onkeydown={(event) => (key = event.key)} />
それ以外のターゲットや、条件が成り立つ間だけ購読したいときは、svelte/events の on() を $effect から返します。on() はリスナを外す関数を返すので、それがそのまま effect の cleanup になります。
import { on } from 'svelte/events'
$effect(() => {
if (!listening) return
return on(window, 'keydown', (event) => (key = event.key))
})
VueUse に同じ形の useEventListener があります。ターゲットを ref か getter で受け、ターゲットが変わると付け直します。この hook と違い、ターゲットを省略すると window を購読します。
import { useEventListener } from '@vueuse/core'
useEventListener(window, 'keydown', (event) => (key.value = event.key))
途中でリスナを外す
戻り値の関数はリスナを外します。ターゲット・イベント名・オプションが変わるまで外れたままなので、再レンダーしても戻りません。
const stop = useEventListener(() => window, 'pointermove', (event) => {
if (done(event)) stop()
})
オンとオフを切り替えるものなら、ターゲットを null にするほうを勧めます。リスナがあるかどうかがレンダーに書かれるためです。