4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React でコーディングする際に気をつけていること

Last updated at Posted at 2024-05-07

細かいチェック

  • 英語を正しく使いましょう
  • 表示内容に依存したコンポーネント名ではなく、パーツとしての命名をしよう
  • keyはトップレベルに当たっているか
  • keyは一意であれば、なんでも良い
  • 定義済みの共通コンポーネントはないか
  • 不要な export がないか
  • 型定義
    • 形はパスカルケースで
    • 型定義は最小単位で分けて書いてあげると良さそう
    • 詳細と一覧のレスポンスは
  • export default は使わない(export名の縛りがなくわかりずらいため
  • コンポジションを適応できないか/継承を利用していないか
  • page コンポーネントではない場合は NextPage ではなく React.FC を使おう
  • レンダリングするパーツが多い時は memo 化することを検討してみても良いかもしれない
  • コンポーネントの中でコンポーネントを定義していないか

コーディング時の 設計 / 注意

  • ContainerとSectionに分け、ロジックはコンテナに集約したい
  • コンポーネントに対して適切な props を与えよ、Responseを与えるな
  • <a href="#"></a> タグをボタンがわりに使わない方が良い
  • mapを使うなら 中のコンテンツを別の ファイル/コンポーネント に分けても良いかも
    • 別のファイルにする条件: export して別の部分から呼び出す場合に限ろう
    • 同一ファイル内で定義することで別の部分で参照されないことが明示的になる
  • 最低限必要な定義に絞ろう
  • 取得できているデータは極力使いまわして、APIを叩かないようにしよう(一覧と詳細API
  • class名を複数設定するときは classnames cx を利用しよう import cx from 'classnames';
  • useCallback レビューチェックリスト useCallbackは、子コンポーネントに渡される関数や、useEffectの依存配列に含まれる関数に適切に使用されているか。依存配列は正しく設定されており、必要な値のみが含まれている
  • Selector の利用を検討できる場所はないか?

useMutation

  • axios を利用せず useMutation を基本的には利用したい
  • async (e: React.FormEvent) ではなく useMutation(XX, {onSuccess: ()を使う

useQuery tanstack

  • Response の data について
    • data: data (データは別名にせず、dataのまま受け取った方が暗黙値でわかりやすい
      • ちなみに data: data は無意味なので data とだけ書いた方が良い
    • 変数名を意図的に変える必要はない、素直に抽象化されたdataで良い
  • enabled に別の通信で取得されるデータを入れ子にすると、そのデータを待つ為、water fall的な処理になって遅い
  • staleTimeとgcTime、デフォルトは永遠に fresh で gcTime は 5分 (5 * 60 * 1000ms)
  • select でデータを加工できる
    • 関数を select に渡し、加工が可能
    • サーバサイドで加工すべきとも思うけれど、そうもいかない場合に有効

以下を確認して、より簡略化できないか検討する。

queryKey: クエリを一意に識別するためのキー。文字列や配列を使用できます。
queryFn: 実際にデータを取得する非同期関数。
select: データ取得後に実行される関数。取得したデータを変換したり、必要な部分だけを抽出したりできます。
enabled: クエリを実行するかどうかを制御するブール値。条件付きでクエリを実行する場合に使用します。
staleTime: データが「古い」とみなされるまでの時間(ミリ秒)。この時間内は再フェッチが行われません。
cacheTime: キャッシュされたデータを保持する時間(ミリ秒)。
retry: エラー時の再試行回数。
refetchInterval: 自動的に再フェッチを行う間隔(ミリ秒)。
onSuccess: データ取得が成功した時に実行されるコールバック関数。
onError: エラーが発生した時に実行されるコールバック関数。

戻り値

const { 
  data, error, status, fetchStatus, isPending, isSuccess, 
  isFetching, isPaused, isError, isLoading
} = useQuery()
  • data: デフォルトは undefined、取得できたらデータが入る
  • error: デフォルトは null
  • status
    • pending キャッシュデータがなく、クエリ実行が完了していない
    • error クエリでエラーが発生した場合
    • success 表示するデータが存在する時
  • fetchStatus fetch の進行状況
    • fetching 現在実行中
    • paused 実行しようとしているが一時停止中
    • idle 実行されていない状態
  • isPending
    • キャッシュもデータもなく、クエリ実行中(初期レンダリング前など
    • status == pending と同じ
  • isFetching
    • 現在データを取得している状態
    • fetchStatus == fetching
  • isLoading
    • status が pending であり
    • fetchStatus が fetch なら True になる
  • Loadingコンポーネント の表示に関してはどうするか
    • 基本的には isPending を使うと良いかもしれない
    • 再取得中は既存データを表示
    • キャッシュもデータもない場合に Loading を表示したいので isPending が良さそう?

enabledオプションを使用してクエリの依存関係を構築したケースを想定します。クエリが無効化されている間フェッチは走らずisFetchingはfalseになります。つまり、isLoadingはtrueになりません。しかし、クエリの実行は完了しておらず表示するデータは存在しないため本来はLoadingの表示をしたいはずです。

isLoadingという一見そのままLoading表示のためのフラグとして使えそうな値ですが定義を理解せずに使うと意図しない挙動になってしまいます。isPendingとの違いを理解しながら使い分けていきましょう。

refetch に関して

  • refetchOnMount
    • マウント時に refetch するか
  • refetchOnWindowFocus
    • ウィンドウを focus した際に refech するか
      • 別ウィンドウから作業していて、戻ってきた時に古い情報を表示していると問題なアプリ(FXトレードとかかな)
  • refetchOnReconnect
    • 再接続時に refetch するか
      • ネットワークが切れた時の挙動を制御するかもしれない

false: 該当のタイミングで refetch がされなくなる
true: キャッシュが stale 状態の場合 refetch する
always: キャッシュの状態に関わらず毎回 refetch する

refetchOnMount: boolean | "always" | ((query: Query) => boolean | "always")

参考

use effect 本当に必要?

そのエフェクトは不要かも?

Stateとして値を持っているのを、Effectで監視するのは2重に管理してしまっている状態でよろしくない。
チュートリアルにも記載がある通り、やりがちだが避けようのパターン。

既存の props や state から計算できるものは、state に入れないでください

  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);

コンポジションという考え方

深くなりすぎてしまうコンポーネントの依存関係を浅くすることができる

 【コンポジションがアンチパターンになるかもしれない話】

コンポジションでchildrenをレンダリングしても良いのですが、素直に children の場所にコンテンツをレンダリングする別のコンポーネントを定義して読み込ませた方が綺麗になる場合があるので、そこを注意しましょう。

childrenを利用せず、素直に内容を取得するコンポーネントを読んだ方がいい。
export const ModalWrapper: React.FC<Props> = ({ closeModal }) => {
  return (
    <Modal>
      <ModalContent />
      <button onClick={() => closeModal()}>閉じる</button>
    </Modal>
  );
};

const ModalContent: React.FC<> = () => {
  const { data, isLoading, isError } = useQuery({queryKey: ['/data'], queryFn: getData,});
  if (isLoading) {return <>データの取得中...</>;}
  if (isError) {return <>失敗しました</>;}
  return (<>メインコンテンツ</>);
};

【本題に戻る】

中身のコンテンツだけ異なっていて、他は同じ場合
あらかじめコンポジションを親要素側で定義し、利用することで
共通化することが出来そう。

  • 無駄なコンポーネントを挟まない
  • コンポジション (ReactNode 型 Props) を使う
  • ステートを適切なコンポーネントに置く

引用元: https://qiita.com/honey32/items/4d04e454550fb1ed922c

Facebook では、何千というコンポーネントで React を使用していますが、コンポーネント継承による階層構造が推奨されるケースは全く見つかっていません。

props とコンポジションにより、コンポーネントの見た目と振る舞いを明示的かつ安全にカスタマイズするのに十分な柔軟性が得られます。コンポーネントはどのような props でも受け付けることができ、それはプリミティブ値でも、React 要素でも、あるいは関数であってもよい、ということに留意してください。

その他: https://ja.legacy.reactjs.org/docs/composition-vs-inheritance.html

参考例

const ModalWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  return (
    <Modal onClose={closeTemplatesModal}>
      {children}
      <button onClick={() => closeTemplatesModal()}>閉じる</button>
    </Modal>
  );
};

export const Contents: React.FC<Props> = ({ closeModal }) => {
  const { data, isLoading, isError } = useQuery({queryKey: ['/data'], queryFn: getData,});

  if (isLoading) { return <ModalWrapper>loading</ModalWrapper> };
  if (isError) { return <ModalWrapper>Error</ModalWrapper> };
  return (<ModalWrapper>メインコンテンツ(通常)</ModalWrapper>);
};

ref の活用

タイムアウト ID の保存、JSX を計算するために必要ではないその他のオブジェクトの保存
このような場合には set しても値が変わらない
常にミュータブルな ref を使うこともできそうです。

DOMの操作方法

dom 要素に対して更新はないが、参照するような場合 ref を活用可能。
ref= で 生成した ref を渡しておくことで、DOM操作を可能とし
focus を当てることができる

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}

メモにするか悩んだら

こうやって計測してみても良い
ただ、大体の場合においてメモ化が必要になることは少ない。

  • コンポーネントを視覚的にラップしている場合、子のレンダーが不要と伝えたい場合
  • コンポーネント内にある state を優先して、上位パーツでステートを管理する リフトアップを行わないようにする。formのパーツ、テキストボックスのホバーなど頻繁に変わるようなstate は 親にリフトアップせずにローカルで保持すべき。
メモにするか悩んだら。
console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');

React Hooks ってなによ

  • useから始まるやつ全般。
  • 再利用可能、カスタムフックを利用することでロジックがカプセルかされ、異なるコンポーネントの間で再利用が可能。
  • UIロジックをコンポーネントから分離
    • useStateとuseEffect、これも再利用性とUIロジック分離の一例ですよね
  • テストを簡単に作ることができる
  • 抽象化が容易である

逆にこのようなものを作れるし、作ることを意識しつつ設計したい。

4
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?