概要
Stateの数値に上限や下限など、制限をつける方法についてです。
クラスだったらコンストラクタ内、setter内で制限をかけたりできるのですが、Stateでも同じようなことができないかと思い、調べ、その方法をまとめました。
ざっくり説明すると、カスタムフックを作成、その中でset関数をラップした関数を作成、そこで値をいじることでうまくできます。
(カスタムフック使わなくとも、そのままラップ関数を作ってできはしますが、set関数が直接使われないように自作フックを使った方が良さそうです)
コード例
以下の例では、カラーコード(RGB値)を State として持たせています。
RGBが取りうる値は 0 ~ 255 なので、それ以外の数値にならないように制限をかけています。
(細かいところはコード内で説明を記載しています)
- useRGB.jsx
import { useState } from "react";
// RGB型
type ColorRGB = {
red: number;
green: number;
blue: number;
};
// 初期化用定数
const INITIAL = {
red: 0,
green: 0,
blue: 0,
} as const satisfies ColorRGB;
export default function useRGB() {
const [colorRGB, set] = useState<ColorRGB>(INITIAL);
/**
* RGB数値の範囲外にいかないように制限する処理(制限用関数)
*/
function clamp(colorNum: number): number {
// 0より小さいなら0に変換し、255より大きいなら255に変換する
return Math.max(0, Math.min(255, colorNum));
}
/**
* RGBの値を変更する処理
* (ここで更新時に制限用関数を使って数値に制限をかける)
*/
function setColorRGB(colorRGBValue: ColorRGB) {
set({
red: clamp(colorRGBValue.red),
green: clamp(colorRGBValue.green),
blue: clamp(colorRGBValue.blue),
});
}
return { colorRGB, setColorRGB };
}
- Color.tsx
import { useMemo } from "react";
import useRGB from "@/hooks/useRGB";
export default function Tmp() {
// 自作フック
const { colorRGB, setColorRGB } = useRGB();
/**
* ボタンクリック時処理
*/
function handleClick() {
// クリックする度+30される
// ただし制限により、255以上にはならない
const newRGB = {
red: colorRGB.red + 30,
green: colorRGB.green + 30,
blue: colorRGB.blue + 30,
};
setColorRGB(newRGB);
}
/**
* カラーコードの RGB → 16進数 変換(見た目で分かりやすくする用)
*/
const colorHex: string = useMemo(() => {
// 各値を16進数に変換し、2桁になるように0埋めする
const { red, green, blue } = colorRGB;
const toHex = (value: number) => {
return value.toString(16).padStart(2, "0");
};
return `#${toHex(red)}${toHex(green)}${toHex(blue)}`.toUpperCase();
}, [colorRGB]);
return (
<div
style={{
margin: 8,
}}
>
{/* クリックする度に白くなるボタン */}
<button
onClick={handleClick}
style={{
backgroundColor: colorHex,
width: 100,
height: 100,
border: "2px solid #111",
borderRadius: 8,
}}
></button>
<p>{`Red:${colorRGB.red}`}</p>
<p>{`Green:${colorRGB.green}`}</p>
<p>{`Blue:${colorRGB.blue}`}</p>
</div>
);
}
- 動作