useSearchBarというカスタムフックを作って上下矢印キー操作からアイテムを操作してみよう
こんにちわ。nap5です。
useSearchBarというカスタムフックを作って上下矢印キー操作からアイテムを操作してみたので紹介したいと思います。
前回のカスタムフックの記事と関連しています。
少し前ですが、以下の検索バーの記事とも関連です。
デモサイトです。上下矢印キーでアイテム選択後、選択した状態でEnterキーを押下すると、今回のデモだと別ページへ遷移します。
以下はコードの抜粋になります。まずはストア側の定義なります。
アイテムのアクティブインデックスが保持できればOKです。
import { atom } from 'recoil'
import { z } from 'zod'
const SearchBarSchema = z.object({
activeIndex: z.number(),
})
export type SearchBar = z.infer<typeof SearchBarSchema>
const searchBarState = atom<SearchBar>({
key: 'searchBar',
default: {
activeIndex: 0,
},
})
export { searchBarState }
次にフック側です。react-useライブラリのuseKeyPressからポートしてキーダウンとアップ、そしてEnterキーに対してアクションを定義しています。
アイテム選択時にEnterキーを押下すると、今回のデモだと別ページへ遷移します。
import { useEffect, useMemo } from 'react'
import { useRouter } from 'next/router'
import { useKeyPress } from 'react-use'
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { searchBarState } from '@/features/film/stores/searchBar'
import { FilmsData } from '@/features/film/types'
const useSearchBar = (data: FilmsData) => {
const router = useRouter()
const setSearchBar = useSetRecoilState(searchBarState)
const activeSearchBar = useRecoilValue(searchBarState)
// https://github.com/streamich/react-use/blob/master/docs/useKeyPress.md
const [isPressedArrowDown] = useKeyPress('ArrowDown')
const [isPressedArrowUp] = useKeyPress('ArrowUp')
const [isPressedEnter] = useKeyPress('Enter')
const [isPressedEscape] = useKeyPress('Escape')
const maxSize = useMemo(() => {
if (!data) {
return 0
}
return data.length
}, [data])
const { activeIndex } = useMemo(() => {
return { ...activeSearchBar }
}, [activeSearchBar])
useEffect(() => {
if (isPressedEnter) {
if (activeIndex !== null && activeIndex !== undefined && data) {
router.push({
pathname: `/films/${data[activeIndex]?.id}`,
})
}
}
}, [isPressedEnter, activeIndex, data, router])
useEffect(() => {
if (!isPressedArrowUp) {
return
}
setSearchBar((prevState) => {
return {
activeIndex:
prevState.activeIndex !== 0 ? prevState.activeIndex - 1 : maxSize - 1,
}
})
}, [isPressedArrowUp, setSearchBar, maxSize])
useEffect(() => {
if (!isPressedArrowDown) {
return
}
setSearchBar((prevState) => {
return {
activeIndex:
prevState.activeIndex !== maxSize - 1 ? prevState.activeIndex + 1 : 0,
}
})
}, [isPressedArrowDown, maxSize, setSearchBar])
return useMemo(() => {
return {
activeIndex,
isPressedArrowDown,
isPressedArrowUp,
isPressedEnter,
isPressedEscape,
}
}, [
activeIndex,
isPressedArrowDown,
isPressedArrowUp,
isPressedEnter,
isPressedEscape,
])
}
export default useSearchBar
最後にコンポーネント側でアクションインデックスを参照しておしまいです。
const Films = () => {
const { data, error, refetch } = useListUpFilmHook()
const { activeIndex } = useSearchBar(data)
return null
}
export default Films
いろいろ応用してみると使いどころがあると思います。
簡単ですが、以上です。
この記事が気に入ったらサポートをしてみませんか?