render hooksパターンを活用したツールチップの実装
概要
MUIが提供しているツールチップを、render hooks
パターンを用いてカスタムフックとして提供したいという内容です。
render hooks
パターンについては以下の記事で紹介されているので是非読んでみてください!
実装したいもの
今回はMUIのツールチップをrender hooksパターンを用いて提供してみます。
By default disabled elements like <button> do not trigger user interactions so a Tooltip will not activate on normal events like hover. To accommodate disabled elements, add a simple wrapper element, such as a span.
(引用元: React Tooltip component - Disabled elements - Material UI)
MUIのツールチップは内部の要素が無効化されている場合は、ホバーなどの動作をしてもツールチップが表示されない仕様があります。解決方法としては内部の要素をspanタグで囲う方法が取り上げられていました。
またツールチップの内部の要素が無効化されている場合のみツールチップを表示させたい場合は、ツールチップの挙動を一部上書きする必要があります。
render hooksパターンを使わない実装例
先ほど記載したツールチップを普通に実装してみます。
このコンポーネントに対してボタンのロジックを追加すると、結構ごちゃごちゃになってしまいます。またツールチップのロジックが呼び出し元に漏れてしまい少しつらくなります。他にもspanタグでボタンが囲う部分に対してはMUIのツールチップの仕様であるというコメントを付けないと意図が分かりにくい状態でもあります。
const TooltipButton = () => {
const disabled = true
const [opened, setOpened] = useState<boolean>(false)
const open = () => setOpened(true)
const close = () => setOpened(false)
return (
<Tooltip
open={!disabled && opened}
onOpen={open}
onClose={close}
title={<Typography>ツールチップを表示するよ</Typography>}
>
<span>
<Button disabled={disabled}>ボタン</Button>
</span>
</Tooltip>
)
}
render hooksパターンを使う実装例
普通に実装すると先ほど述べたようにつらい部分が出てきてしまいます。render hooksパターンではこういったロジックであったりコンポーネントの仕様をカスタムフックに閉じ込めることが出来ます。
以下は実装例になります。
interface RenderToolTipProps {
disabled: boolean
message: string
children: JSX.Element
}
export const useCustomTooltip = () => {
const [opened, setOpened] = useState<boolean>(false)
const open = () => setOpened(true)
const close = () => setOpened(false)
const renderTooltip = ({
disabled,
message,
children,
}: RenderToolTipProps) => {
return (
<Tooltip
open={!disabled && opened}
onOpen={open}
onClose={close}
title={<Typography>{message}</Typography>}
>
<span>{children}</span>
</Tooltip>
)
}
return renderTooltip;
}
次に実際にツールチップを呼び出すコンポーネントの実装例です。
render hooksパターンを使わない実装に比べて、ツールチップに関するロジックが存在しない点や、ボタンのロジックを追加してもメンテがしやすそうに見えます(感想)
またMUIのツールチップの仕様を取りやめる場合でも、useCustomTooltip
を改修するのみで済むので旨味が大きいのでは?と思っています。
const TooltipButton = () => {
const disabled = true
const CustomTooltip = useCustomTooltip()
return (
<CustomTooltip disabled={!disabled} message={"ツールチップを表示するよ"}>
<Button disabled={disabled}>ボタン</Button>
</CustomTooltip>
)
}
まとめ
本記事では、MUIのツールチップをrender hooksパターンを用いてカスタムフックとして切り出す話をさせていただきました。本記事を通してrender hooksパターンのパワーを少しでも感じていただければ幸いです。
Discussion