はじめに
Reactで<input type="date" />
を使って、日付を選択するフォームを作った時に、2月31日
とかの無効な日付も選択できてしまうのを防ぎたいと思ったので実装してみました。
inputタグだと味気ないので、material-uiのTextFieldコンポーネントを使ってますが、機能自体は同じです。
日付選択を作る
date
で日付の状態を管理します。
value
にdate
を受け渡して、onChange
イベントを受け取って状態を更新する普通の処理です。
App.js
export default function App() {
const [date, setDate] = useState("");
return (
<div className="App">
<FormControl variant="outlined">
<TextField
variant="outlined"
type="date"
value={date}
onChange={e => onChangeDate(e}
/>
</FormControl>
</div>
);
}
とりあえずonChangeDate
はそのまま状態を更新してみる。
App.js
const onChangeDate = (e) => {
setDate(e.target.value);
}
```
↑キーとかで選択すると、こんな風に無効な日付が選択できてしまうんですよね・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619552/4c5d59e3-3610-2067-0c98-2856ccfe1d9b.png)
## onChangeDateでいい感じに調整する
問題点としては
* 2月は閏年があるので、28or29日
* 4/6/9/11月は30日まで
なので、一個前の状態`date`を参照して、書き換えていきます。
無効な日付の時の`e.target.value`には値が入ってないので、値が入っている時はそのまま状態を更新します。
それ以外の場合は月ごとに分岐して書いていきます。
``````js:App.js
const onChangeDate = (e) => {
if (e.target.value) {
setDate(e.target.value);
} else {
const nowYear = date.slice(0, 4);
const nowMonth = date.substr(5, 2);
const nowDate = date.slice(-2);
if (nowDate !== "01") {
setDate(`${nowYear}-${nowMonth}-01`);
}
else{
switch (nowMonth) {
case "02":
if ((nowYear * 1) % 4 === 0) {
setDate(`${nowYear}-${nowMonth}-29`);
} else {
setDate(`${nowYear}-${nowMonth}-28`);
}
break;
case "04":
case "06":
case "09":
case "11":
setDate(`${nowYear}-${nowMonth}-30`);
break;
default:
break;
}
}
}
}
```
これで無効な日付がとりあえず入らないようになりました!!
ただ、これだと`3/31`から月を2月に変更(↓矢印)すると、`3/1`に一回なってしまいます。
ちょっと気持ち悪いですね。。。
しかし、`e.target.value`に値が入ってこないのですし、年月日のどの値を変更したかもわからないため、
どうしようもなさげです。
世に出回っているカレンダーアプリとかの日付は、普通に2/31日とか選択できるようになっていて、保存できないようにしていたりするのでこれがベストプラクティスなのでしょうかね。
## まとめ
今回は日付選択の無効な日付を表示させないようにしてみました。
まだ気持ち悪いのでベストプラクティスをご存じの方はコメントをお願いします!!