はじめに
普段業務ではReactを使っているのですが、レビューをしていると時々「そのuseStateは冗長じゃないかな」と思うコードを見かけることがあります。私も学び始めの頃はそんなコードをたくさん書いていました。
二番煎じどころかもはや出涸らしになったネタにはなりますが、今回は業務で見かけた不要なuseStateの例とそのbefore/afterをご紹介します。
より詳しく知りたい方は公式ドキュメントをご確認ください。
不要なuseStateの例
stateを別のstateに変換して保持する
公式ドキュメントにも書かれていますが、既に定義されたstateから計算できるものを別のstateで保持する必要はありません。
以下の例ではtextをuseEffectの依存配列に指定して、textが更新されるたびに文字列の長さを求めています。その結果を別のlengthというstateで保持している状態です。
before
export const SamplePage = () => {
const [text, setText] = useState<string>('');
const [length, setLength] = useState<number>(0);
useEffect(() => {
setLength(text.length);
}, [text]);
return (
<Box>
<Typography>文字数:{length}文字</Typography>
<Input value={text} onChange={(e) => setText(e.target.value)} />
</Box>
);
};
こちらはシンプルにtext.length
と書くことで文字列の長さを求めることができます。もっと長く複雑な処理で計算結果を求めることもありますが、その場合は関数化するか重い処理ならuseMemoを使うと良いと思います。
after
export const SamplePage = () => {
const [text, setText] = useState<string>('');
return (
<Box>
<Typography>文字数:{text.length}文字</Typography>
<Input value={text} onChange={(e) => setText(e.target.value)} />
</Box>
);
};
propsをstateに変換して保持する
こちらも先程と同じような例になります。親コンポーネントから渡ってきたpropsに対して何らかの処理をしたくなることがありますよね?その場合にpropsをuseEffectの依存配列に指定して、計算結果を新しいstateで保持して表示...と書きたくなってしまうかもしれません。今回の場合であればstatusIdからステータスのラベルを求めてstatusLabelで保持した結果を表示しています。
before
export const SamplePage = ({ statusId }: { statusId: number }) => {
const [statusLabel, setStatusLabel] = useState<string>('');
useEffect(() => {
const label = (() => {
switch (statusId) {
case 1:
return '待機中';
case 2:
return '実行中';
default:
return '無効';
}
})();
setStatusLabel(label);
}, [statusId]);
return (
<Typography>{statusLabel}</Typography>
);
};
この場合もpropsから直接計算結果を求めることで、新しいstateを定義する必要がなくなります。元々見かけたコードが重めの処理だったのでuseMemoを使用していますが、今回ぐらいの軽い処理であればgetLabelのような関数を作成して呼び出す形でも良いと思います。
after
export const SamplePage = ({ statusId }: { statusId: number }) => {
const statusLabel = useMemo(() => {
switch (statusId) {
case 1:
return '待機中';
case 2:
return '実行中';
default:
return '無効';
}
}, [statusId]);
return (
<Typography>{statusLabel}</Typography>
);
};
スタイルをstateで制御する
例えばですがホバーした時だけ色を変えたり何かを表示したりすることがあると思います。ホバーはあくまでも一例ですがこの場合はstateを使って以下のような書き方をすることができます。ですがこれは何でもstateで制御することに囚われ過ぎています。私もuseStateこそが最強でこれさえあれば何でも作れると思っていた時期がありました。
before
export const SamplePage = () => {
const [isHover, setIsHover] = useState<boolean>(false);
return (
<Box
sx={{
backgroundColor: isHover ? 'blue' : 'white',
}}
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
>
ホバーする要素
</Box>
);
};
ここで思い出していただきたいのですが、ホバーした際に色を変えるだけならcssなどのスタイル側で書くことができます。今回はsxを使ってコンポーネントの中にスタイルを記載していますが、以下のように通常時とホバー時のスタイルを書くことでホバーした時に背景色を変更することができます
after
export const SamplePage = () => {
return (
<Box
sx={{
backgroundColor: 'white',
'&:hover': {
backgroundColor: 'blue',
},
}}
>
ホバーする要素
</Box>
);
};
おわりに
Reactを使っていると色々と柔軟な書き方ができるので、見返してみると冗長なコードになっていることもあると思います。特にuseEffectとuseStateを使えばどんな形であれ多くの機能は実現できますよね。学び始めの頃や個人開発の場合はそれで特に問題はありませんが、この先何年も運用するプロダクトの場合は後々メンテナンスが大変になってしまいます。「最強の機能が完成したぞ」という時ほど無駄なstateを使用していないかなと見返していただければと思います。