きっかけはIDから始まった
カレンダーメモアプリをReactで作り直していて、編集機能を付けようとしたときのこと。
次はIDの初期値を入れるんやな、じゃあ
const[editingId,setEditingId]=useState("")でええか!
クロード「ダメです、違います。nullです。」
ワイ「ほえ?じゃあ、0は?」
クロード「ダメです、違います。nullです。IDはまだ何も存在していないからです。
また、IDが1から始まってるからいいですが、0から始まった時は壊れます。」
ワイ「じゃあ、なんも入れない」
クロード「動くけど、設計なのかどうなのかわからんから不親切、却下。」
ワイ「ふぁ」
かくして、ドツボにハマりました。
背景
今回は、新規作成フォームと編集フォームで、同じinputを使い回す設計にしていました。
だとすると「今どっちのモードなんか」をどこかに覚えとかないといけない。
その作業台がeditingId
•編集中 → そのメモのIDが入っている。
•何もしてない → 「無い」が入っている
で、条件分岐させる。
問題は下の「無い」をどう書くかでした。
ないのにあるって宣言。
感覚としてはわかるけど、微妙にモヤる。
自分向いてないなと思いながらクロードと壁打ちして納得できました。
他はなぜダメなのか?
エントリー1 ([])
ダメな理由:IDだから保存するのは1個の値 配列は複数個入れるのが普通なので過剰というか型の不一致
エントリー2(””)
ダメな理由:IDは数字しか入らない ””は文字列。動くことは動くが可読性が悪いためNG
エントリー3(0)
ダメな理由:上で書いた通り、IDが0から始まった時が壊れる
エントリー4()何も入れない undefind
ダメな理由:設計なのか何なのかわからん。空を返すならnullと明示しろ。
じゃあ初期値は毎回nullでええやん。
…とはならないというのが答えです。
const [title, setTitle] = useState(""); // 入力欄の中身
const [memoCount, setMemoCount] = useState(0); // 件数
const [isLoading, setIsLoading] = useState(true); // 読み込み中か
const [editingId, setEditingId] = useState(null); // 編集対象
memosが[]なのは「一覧は最初から存在してて中身が0件」だから。
titleが""なのは「入力欄は画面にあって中身が空」だから。
memoCountが0なのは「件数が0件ある」という状態だから。
isLoadingがtrueなのは「画面を開いた瞬間から取得が始まる」から。
editingIdがnullなのは「編集という行為がまだ起きていない」から。
条件によって使い分けが起きる。
また読むときにどれだけ気を配れるかってのが大切なんだなと学びました。
最後に
もやっている方へ
この問題を解いてみてください
こんな感じでシュチュエーションを考えるとなんとなく僕は見えてきました。
問 それぞれの初期値useState() の()には何が入るでしょうか?
Q1.memoCount — メモの件数を表すとき
Q2.searchKeyword — 検索ボックスに入力された文字を表すための初期値
Q3. tags — メモに付けるタグの一覧。 タグは何個でもつけれます(例:["仕事", "私用"])
Q4.errorMessage — 通信失敗時にエラー文を表示するための状態。2パターン答えあります。
Q5.最後に保存が成功した日時。まだ一度も保存していない状態から始まります。日時はDateオブジェクトで持つとします。 (実務によってこれの処理は変わるかもしれません)
答え
Q1. memoCount → useState(0)
件数は数値。まだ何もない状態なので 0。
Q2. searchKeyword → useState("")
入力文字は文字列。何も入力していないので空文字。
Q3. tags → useState([])
複数のタグを持つので配列。最初はタグなしなので空配列。
Q4. errorMessage → useState("")(または useState(null))
初期状態ではエラーがないので空文字。「エラーが無い=null」と表現したいなら null でもよい。表示ロジックが「空なら出さない」なら "" が扱いやすい。
Q5. 最後に保存した日時 → useState(null)
まだ一度も保存していない=「値が存在しない」状態なので null。保存が成功したら new Date() を入れる。new Date() を初期値にすると「起動時刻に保存済み」と誤解されるのでダメ。
valueにつながる箱の初期値がnullだと、「値が入っていない入力欄」と「文字列が入っている入力欄」の間でReactの扱いが変わってしまうので""の時もある。
以上です!