react + typescriptのプロジェクトで、 コンポーネントのpropsの値にジェネリクスで動的に型を定義、 forward refで親コンポーネントから特定の関数を任意のタイミングで実行できるようにする時の記述方法をまとめました。
いくつか方法はあるみたいですが、以下の書き方がすっきりしてて良いのかなと思いました。 以下、ソースコードになります。
型定義の更新
import React from 'react'
declare module 'react' {
function forwardRef<T, P>(
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,
): (props: P & React.RefAttributes<T>) => React.ReactElement | null
}
アロー関数ではなく通常のfunctionによる関数定義で実現する場合は、forwardRefの型定義を更新しなければいけないみたいです。
ので、私の場合はindex.d.ts
でReact.forwardRefの定義を更新しました。
コンポーネント
今回のサンプルソースコードです。
Props(SampleListProps)が
import React, { forwardRef, useImperativeHandle, useState } from 'react'
import './SampleList.css'
interface SampleListItem<KEY> {
key: KEY
value: string
}
interface SampleListProps<KEY> {
items: SampleListItem<KEY>[]
onClickItem: (item: SampleListItem<KEY>) => void
}
export interface SampleListRef {
resetSelectItem: () => void
}
function SampleList<KEY>(
{ items, onClickItem }: SampleListProps<KEY>,
ref: React.ForwardedRef<SampleListRef>,
) {
// state
const [currentItem, setCurrentItem] = useState<SampleListItem<KEY> | null>(null)
// handle
useImperativeHandle(ref, () => ({
resetSelectItem: () => {
setCurrentItem(null)
},
}))
return (
<div>
<ul>
{items.map((_item) => (
<li
key={`${_item.key}`}
className={currentItem?.key === _item.key ? 'selected' : 'not-selected'}
onClick={() => {
setCurrentItem(_item)
onClickItem(_item)
}}
>
{_item.value}
</li>
))}
</ul>
</div>
)
}
export default forwardRef(SampleList)
setCurrentItem()
のuseStateがちゃんと動いているか確認のためのcss定義
.selected {
background-color: aquamarine;
}
.not-selected {
background-color: azure;
}
使い方
import { createRef, useMemo } from 'react'
import SampleList, { SampleListRef } from './SampleList'
const Test = () => {
const sampleListRef = createRef<SampleListRef>()
const items = useMemo(
() => [
{
key: 'key1',
value: 'value1',
},
{
key: 'key2',
value: 'value2',
},
{
key: 'key3',
value: 'value3',
},
],
[],
)
const onClickReset = () => {
sampleListRef.current?.resetSelectItem()
}
return (
<div>
<SampleList
ref={sampleListRef}
items={items}
onClickItem={(_item) => {
console.log('onClickItem()', _item)
}}
/>
<button onClick={onClickReset} type='button'>
リセット
</button>
</div>
)
}
export default Test
createRefでSampleListのrefを取得します。 onClickReset()関数の内部で、refを使ってresetSelectItem()関数を呼び出し、SampleListのリセットを行なっています。
以上がソースコードでした。 他にも定義方法がありそうなので、調べてみようかなと思います。
参考
以下のサイトを参考にさせていただきました、ありがとうございました。
https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/