◆ 書くのが面倒
◆ eslint のチェックがあれば多少マシだけど不要なものまで書かないとエラー
◆ useEffect は実行条件と内部で使う変数の更新を分けてほしい
◆ useEvent はまだ実用できるまで遠そう

React 本体の機能で最近特に不満に感じてるところは依存配列です
他ライブラリへの移行を考えるくらいにはストレスになります
結局簡単に移れるいい感じのライブラリ(周辺ライブラリ込)はなくて不満を言いながら React を書いてるのですけど

deps array

React は毎回レンダリング処理でコンポーネント関数を実行します
副作用を起こさなければ何度実行しても同じ結果なのでそれでいいのですが パフォーマンスを考えるとムダな処理を減らしたいことはあります

機能が少なめで軽めの画面なら useMemo や useCallback は不要と言われています
重くなりがちな開発モードでも快適に動きます
アプリケーション全体として大きくなっても 個々のページに分かれていてそれぞれのページが複雑化していなければ useCallback を一切使わなくても問題ありません
実際に以前は極力重くなるようなページにせず ページを分けるなどで対処して useCallback は一切使わないようにしていてましたが特に問題はなかったです
こういう状態なら React への不満は少なく快適にコードを書けます

しかし 重たい画面というのは出てきます
特にライブラリを使わざるを得ないところでは 重ためのレンダリング処理になっていることが多めです
本番モードとしてビルド後は問題無いレベルでも開発中にストレスがたまる程度に遅いことがあります

そこである程度の時間がかかりそうなものは useMemo したり props が変わらないなら再レンダリングをしないように React.memo したりします
そうなってくると useCallback も必要になります
関数は中身が同じでも再作成され参照が変わると別の値です
コンポーネントに渡したとき props が変わったとみなされないよう useCallback を使わないといけません

useMemo や useCallback では前回と同じ値を返しますが 更新が必要な場合もあるのでそれを依存配列に書く必要があります

依存関係

こうなるとすごく書くのが面倒になってきます
useCallback/useMemo 関数の中で使う変数は依存する値なので全て依存配列に記述が必要です

関数 A を関数 B の中で使い 関数 B を useCallback にするなら 関数 A が毎回新しくなると useCallback の意味がないので関数 A も useCallback が必要です
こうして連鎖的にあちこちで useCallback を使わざるを得なくなります

これは必要なのかとか考えるのが面倒ですし 何も考えず全部を useCallback 化すればいいとも思えますがそれはそれでとても面倒です

eslint

よく起きる問題は依存配列の書き間違えです
必要なものが抜けていたら古い値を受け取るので正しく動きません

一応それに対処するためのチェック機能が eslint のプラグインとして用意されています
CRA などのテンプレートから作るとデフォルトで有効になっています

ただ そこまで賢くなくて融通が効かなかったりします

ずっと変わらないものを認識しない

例えばこういうコードがあります

const cb = useCallback(() => {
console.log(1)
}, [])

const cb2 = useCallback(() => {
cb()
}, [])

cb は依存配列が空なのでコンポーネントがアンマウントされるまでずっと同じものです
cb2 は cb を中で使いますが cb は変わらないので 依存配列に入れる必要はなく これもずっと変わらないものとして扱えます
しかし eslint のチェックではこういうことを考慮してくれません
中で使ってるなら依存配列に全部を書けというルールです

これに真面目に従っていると書く量が多くなりとても面倒です
コードが長くなり見づらくもなってきます

なにか方法がないかと以前探したときに ある値を依存配列に入れる必要がないと値にマークできないかとライブラリ作成者が質問していて React 開発のチームからは対応予定はないと答えられていました
たしか 後から変更されて依存配列が追加される場合があるかもしれないから 最初から入れておいたほうが良いみたいな回答でした
言いたいことはわからなくはないですが そういうのを全部書いていくのは苦痛しかないです
それにこれは eslint でチェックされてるから出るメッセージです
eslint が動いてるなら変わったとしてもそのタイミングでエラーが出るようになり気づけるので別に問題にならないと思うんです
そもそも 変わるケースってすごく稀でほとんどないのに そのために多くの場所で面倒なことをしたくないです

自動で依存配列を管理してくれるコンパイラを研究中だとという話を見かけますが もう結構前から言っているのに実用されていないのですからとりあえず先にすぐできる回避策を用意してほしいものです

依存関係がある場合

A が変われば B も変わるみたいなとき A と B の両方を依存配列に入れなくても B だけ入れておけば十分です
そういうのを考えて無駄なく依存配列を書きたいのに eslint のチェックではそういうことが許可されません

ただのチェックなので無視したり 指定行を無視させるとか方法はあります
実際無視してる箇所は多数あります
ただそういうところは変更があったときに影響がないか見直す必要があって 漏れになりやすいです
だからといって eslint の通りに全部を書いていくのは苦痛でしかなくやりたくないものです

オブジェクトと関数プロパティ

オブジェクトのプロパティとして関数が入っている場合も 判定が微妙です

const cb = useCallback(() => {
obj.fn(1)
}, [obj.fn])

中では fn しか使っていないので依存配列には obj.fn を指定しています
しかしこれはエラーで obj を依存配列に加えるよう言われます

obj.fn はめったに変わらないけど obj は毎回変わるというケースはありえます
自作フックで複数の関数をまとめて返すときなどは基本そうなります
そういうときに単純に obj を依存配列に入れてしまうと useCallback の意味がなくなります
回避のためには obj.fn を fn など別の変数に入れてから使う必要があります

const fn = obj.fn
const cb = useCallback(() => {
fn(1)
}, [fn])

一時変数を増やしたくないのであまりやりたい方法ではないです
理由は this によるものとか見た覚えがありますが アロー関数を使っているなどで判定できると思うんですよね

カスタムフック

フックを自作するときには依存配列を動的に作ることがあります
こういう場合もちゃんと判断できないので無視せざるを得なくなります

また useCallback などと同じような使い方ができるように 2 つめの引数に依存配列を受け取るようにすることもあります

const useCustomHook = (fn, deps) => {
useEffect(fn, deps)
}

useCustomHook(() => {
console.log(foo, bar)
}, [foo, bar])

こういう場合にチェックは効かなくなります
React 的には useCallback を使った関数を渡してこういう風にするのを推奨らしいです

const useCustomHook = (fn) => {
useEffect(fn, [fn])
}

useCustomHook(
useCallback(() => {
console.log(foo, bar)
}, [foo, bar])
)

依存配列は組み込みフックの箇所のみしか使わずに済んではいますが 見やすさ書きやすさ的にこれは避けたいものです

順番

依存配列に入れるために関数の順番が決められてしまいます
本来はこういう順序で書くことができます

const a = () => {
b()
}

const b = () => {
//
}

a の定義の時点では b は未定義ですが a が実行されて b を参照する時点で b があれば問題ありません
しかし useCallback が入ると

const a = useCallbac(() => {
b()
}, [b])

const b = useCallback(() => {
//
}, [])

b を関数定義の時点で参照する必要があり これではエラーになります
b を先に持ってこないといけません

参照するものなので先に書くべきというのはわからなくはないですが 並びを気にしないといけなくなるのは面倒です
関連するものをまとめたり 読みやすくするため ◯◯系は先に書いて □□系は後に書くみたいなルールを決めて書けなくなりますし
現状では出くわしてませんが相互に呼び出す関数ってどうするのでしょうね
useMemo 1 つでまとめて定義とか特殊なことが必要なのかもです

useEffect

useEffect も依存配列を使いますが useCallback などと違い依存する値が変わったタイミングで実行もされます
変数の値の変化と実行したいタイミングが異なることはそこそこあります
実行するときに最新の値は取得できてほしいですが 実行したいのは ある変数が変わったときだけで中で使う変数全てではありません

useEffect に渡す関数が引数として前回の値を受け取るなら それと今の値を比較して処理をするしないを分岐できます
しかし そういう機能はなく何が変わったかわかりません

実行されるタイミングで関数は最新になるはずなので この場合 依存配列に書くのは実行するトリガであり 中で使う変数すべてではないはずです
なので eslint でエラーが出ても無視するか無効化コメントを入れることになります
ただこれだと あとあと抜けている変数が意図的なものなのかミスなのかわかりづらいです

これはよくある問題として useEvent で解決されそうで 期待していたのですが 順調進んではいないようです
useCallback をすべて useEvent に置き換えることなりそうなどの不安があるとかで useEvent は正式導入されないようです
experimental ブランチで試すことができるにとどまってます
https://github.com/reactjs/rfcs/pull/220

useCallback は常に最新の状態を参照できていいので useEvent に置き換えるでいいと思うんですけどね

useEvent で useCallback と useEffect がマシになると期待していた分 落胆も大きく React を捨てたいという思いが加速しました