react 2年目がいろいろ学んだことやハマったことの記録。
今回は、「ランダムにキー配置するソフトウェアキーボード」を作った時のお話。
キー配列をシャッフル
キーボードは数字のみと英数字記号の2タイプ。さらにキー配置そのものを変えるものと、あるグルーピング単位で配置を変えるという仕様のキーボードを作ります。
ランダム→配列をシャッフルするから、Array.shuffle() かな?と思いましたが・・・ありませんでした。
みなさん自作なさってるようなのでそれについては真似して作りました。
続けてシャッフル対象のキー配列を作成します。まず「数字のみ」タイプから。
数字のみのほうはキー配置そのものを変える仕様のキーボードです。
もう1つのタイプでも数字は使うので、1〜9の数字と0の配列を1つ別に作っておきます。
数字タイプの、いわゆる10キーは3列4行の配置でレイアウトしたいため、ダミーの空文字を2つ加えて12個のキーを作り、シャッフルします。
シャッフルするタイミングはキーボードを開く時のみにしたいため、depsは未指定にします。
const numbers: string[] = React.useMemo(() => {
return [...Array.from({length: 9}, (_, i) => (i + 1).toString()), '0'];
}, []);
const tenkeys: string[] = React.useMemo(() => {
return shuffle([...numbers, '', '']);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
グループ配列をシャッフル
残りのキーボードは「英字」「数字」「記号」のグルーピング単位で配置を変えます。
例えば「数字」「英字」「記号」とか、「記号」「英字」「数字」とか、そういう配置換えです。
この場合だと、3つの数字をシャッフルすればよいだろう、ということで2次元配列を作成してみました。
こちらも10キーと同様、シャッフルするタイミングは開くときのみです。
const alphabets: string[] = 'ABCDEF...'.split(''); // アルファベット文字列は省略してます
const symbols: string[] = '\"#$%...'.split(''); // 記号文字列も省略してます
const charkeys: stinrg[][] = React.useMemo(() => {
return [[...numbers], [...alphabets], [...symbols]];
}, [numbers, alphabets, symbols]);
const groups: number[] = React.useMemo(() => {
return shuffle([...Array(charKeys.length).keys()]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
実際の10キーのキーボードはこんな感じで作ってみました。
const tenKeyboard = React.useMemo(() => {
return (
<>
<div className="tenkey">
<div className="fixed">
{tenkeys.map((key, index) => (
<div className="keycell" key={index} onClick={() => onClick(key)}>
{key}
</div>
))}
</div>
</div>
</>
);
}, [tenkeys, onClick]);
英数字記号のほうも同様な感じで、ただしこちらは2次元配列のため2回ぐるぐるまわします。
const alphanumKeybord = React.useMemo(() => {
return groups.map((key, i) => {
const charKey = charKeys[key];
return (
<React.Fragment key={i}>
<div className="keySection">
{charKey.map((key, index) => (
<div className="keycell" key={index} onClick={() => onClick(key.toString())}>
{key}
</div>
))}
</div>
</React.Fragment>
);
});
}, [keyboards, charKeys, onClick]);
シャッフルしまくる
今回のハマりどころは、メモ化のdeps指定でした。
初めはあまり深く考えずに、メモ化してeslintに言われるままdepsに影響する変数を書き込んでました。
すると、キーをクリックするたびにシャッフルが走ってしまうキーボードが出来上がってしまいました。
シャッフルは最初の1回だけなのだから、ということで前述した空配列指定になったわけです。ただしこのままだとeslintに怒られますので、無効化にするコメントも一緒に記載します。
メモ化はどのタイミングでメモの内容を書き換えるべきか?をよく考えて使わないといけない、と勉強した作業となりました。