Open34

Reactに関する知見を雑にまとめる用

TatsukiTatsuki

Reactに関して、再勉強が必要だと感じたため
参考になった記事などをまとめるようのスクラップ

TatsukiTatsuki

https://zenn.dev/yend724/articles/20240711-qfbqiba6m9iul2al

データのfetchやZustandで保持しているパラメータの加工などにuseEffectを使わなければならない状態の時があったのだが、適切だったのか検討する

TatsukiTatsuki

前提:
Reactの関数は純関数であること

  • 同じ入力には同じ出力であること (同じ入力が与えられた場合、コンポーネントは常に同じJSXを返す必要がある)
    • トマトサラダを注文した人がオニオンサラダを受け取ってはいけない
  • 自分の仕事に専念する (レンダー前に存在したオブジェクトや変数を変更しない)
    • ある注文が他の誰かの注文を変更してはいけない

参考
https://ja.react.dev/learn/render-and-commit

TatsukiTatsuki

一方で、現実的には副作用が必要になることもある

副作用とは

  1. 関数外に影響を与えること
  2. 引数以外の要因で結果が変わってしまうこと

また副作用という言葉は、Reactに限定された言葉ではないこと

参考
https://qiita.com/Mitsuw0/items/801f783ca74b062c1ed8

TatsukiTatsuki

Reactの副作用を安全に扱うための方法は2つ提供されている

  1. イベントハンドラ: ユーザーのアクションが実行された時にReactが呼び出す関数
  2. useEffect: レンダリングに付随する副作用を扱うReactフック
TatsukiTatsuki

useEffectは避難ハッチであることを理解する

避難ハッチ:

React の「外側に踏み出して」外部システムに接続するための避難ハッチ (escape hatch)
{中略}
アプリケーションのロジックとデータフローの大部分は、これらの機能に依存しないようにすべきです。

https://ja.react.dev/learn/escape-hatches

TatsukiTatsuki

エフェクトは必要ないかもしれない
エフェクトが不要なケース

  1. レンダーのためのデータ変換にエフェクトは必要ない (propsのデータを加工するためのものなど)
  2. ユーザーイベントの処理にエフェクトは必要ない
    https://ja.react.dev/learn/escape-hatches#you-might-not-need-an-effect

もっと詳しい内容。ここを読んで自社のコードの不要なコードは消したい
https://ja.react.dev/learn/you-might-not-need-an-effect

TatsukiTatsuki

そもそもeffectとは?
前提: 副作用はレンダー中には実行してはいけない

ユーザーのアクションによって何かしらの副作用が生じる場合 -> イベントハンドラ
特定のイベントではなく、レンダー事態によって引き起こされる副作用を特定するため -> エフェクト

エフェクトを扱うためのReactフック -> useEffetc

TatsukiTatsuki

useEffectはコンポーネントを外部システムと同期させるためのもの

TatsukiTatsuki

外部システムとは?
Reactによって制御されていないもの

  • ネットワーク
  • 何らかのブラウザAPI
  • サードパーティライブラリやウィジェット
TatsukiTatsuki

エフェクトが行うこと

  1. 同期を開始する
  2. 同期を停止する

多くのケースで同期の停止も必要であること

TatsukiTatsuki

Reactのコードが画面に描画されるまでの話
https://ja.react.dev/learn/render-and-commit

トリガー -> レンダー -> コミット

TatsukiTatsuki
  1. レンダーのトリガー(お客様の注文を厨房に伝える
  2. コンポーネントのレンダー(厨房で注文の品を提供する)
  3. DOMへのコミット(テーブルに注文の竹刀を提供する)
TatsukiTatsuki
TatsukiTatsuki

このように書いて、
handoClickを実行してもageは1しか増加しない

  const [age, setAge] = useState<number>(0)

  const handleClick = () => {
    setAge(age + 1)
    setAge(age + 1)
    setAge(age + 1)
  }

理由: 最後の処理が完了したタイミングで、バッチ処理で更新され、再レンダリングされる

TatsukiTatsuki

これであれば更新される

  const [age, setAge] = useState<number>(0)

  const handleClick = () => {
    setAge((state) => state + 1)
    setAge((state) => state + 1)
    setAge((state) => state + 1)
  }
TatsukiTatsuki

コンソールに2回レンダリングされる理由
React StrictModeの場合だと呼び出される(開発中のみ)
・純関数である必要があること
・純関数であるかどうかをチェックするために2度レンダリングしている

Strice ModeをOnにしておくこと

TatsukiTatsuki
onClick={handleClick}

のケースと

onClick={()=>handleClick}

の違い

  • onClick={handleClick}
    • コンポーネントが再レンダリングされても関数の再生性がおこなわれない
  • onClick={()=>handleClick}
    • コンポーネントが再レンダリングされると、関数の再生成がおこなわれる
    • 引数を受け取る場合にはこっち

基本的にはどちらでも良いとのこと
引数がなければ、関数を渡して、引数があればアロー関数で描くのもありか

TatsukiTatsuki

Reactのレンダーとは
VDOMの差分チェックをすること

差分分だけコミットする

TatsukiTatsuki

useRef: 値を保持するフックス。useStateと異なるのは、値が更新されてもコンポーネントが再レンダリングされない

useStateなどでコンポーネントが再レンダリングされても、useRefの値は保持されている

TatsukiTatsuki

余計なコンポーネントの再レンダリングを防ぐことができる

DOMを操作するときに使用することが多い

TatsukiTatsuki

React.memo(memoだけでもimportすれば使用可能)

子コンポーネントが親コンポーネントの再レンダリングによって、余計な再レンダリングされることを防ぐ(propsが変わらなければ、子コンポーネントが再レンダリングされない)

ただし、何でもかんでもmemo化するのは得策ではない
memo化を検討するケース

  • 画面レンダリングが高負荷であり、画面がちらつくなどパフォーマンスに影響があるケース
    • 例: D&D で画像を移動させるFigmaのようなもの
  • 親コンポーネントが再レンダリングされることで、子コンポーネントのPropsが変換しない場合
    • 子コンポーネントへのPropsが変化する場合には、memo化してもあまり効果が得られない
TatsukiTatsuki

useCallBackを含むパフォーマンスチューニングについては基本的には最初は実施が不要
検討するケースとしては、

  • アプリケーションのFE側の処理のパフォーマンスに影響が出た
  • auth情報など、様々な画面で使用するケースが想定されるusecase関数
    などがあるときには使用を検討する
TatsukiTatsuki

useCallBack
再レンダリングによって、親コンポーネントから子コンポーネントに関数をpropsで渡している場合、関数が再生成されることで、子コンポーネントも再レンダリングされる

TatsukiTatsuki

useCallBack: 関数の再生成を防ぐ
useMemo: 値の再計算を防ぐ

TatsukiTatsuki

思想によるが、
カスタムフックスには、無条件でuseCallBackやuseMemoを使用しても良いのではとのこと