Yuri's Blog
クリスマス帽子のレトリバーさん

Todoアプリのハンズオンで学んだことを整理する

公開日:2024年12月22日

最終更新日:2024年12月22日

React TypeScript

こんにちは、Yu_riです。
今回は「React Hooks と TypeScript でつくる Todo PWA ~ 入門 React ハンズオン」で自分が学んだことを以下の4セクションに分けて整理していきます。

  • HTML
  • JavaScript
  • React
  • TypeScript

HTML


autofocus属性
【概要】
・autofocus属性をtrueにできるのは1つのhtmlファイルにつき1つまで
【注意点】
・autofocus属性をtrueにできるのは1つのhtmlファイルにつき1つまで
※複数の要素に設定されても最初のものにフォーカスが当たる

JavaScript


オブジェクトのプロパティ名を動的にする
【概要】
・プロパティ名にブランケット記法を使用し、その中に記述した式の戻り値をプロパティ名として使用することができる
【使用例】

const sk = "sampleKey";
const sampleObj = {
	[sampleKey]: "sampleValue"
};
console.log(sampleObj); // {sampleKey: sampleValue}
console.log(sampleObj[sk]); //プロパティ値へのアクセス

Tree Shaking
【概要】
・実行されないコードを削除する技術
【実行タイミング】
・webpackなどのバンドラーが複数のJSファイルを1つにまとめる際に行う
・import/exportを利用してエクスポートされたコードがJSファイルで使用されているかを調べる

React


createRoot
【概要】
・ReactがどのDOM要素(ルート要素)を管理するかを指定するための関数
・Reactの非同期でのレンダリングが有効になる
参考)https://zenn.dev/nawomat/articles/f8be31b66bfc19
・createRootを使ってマウントしたDOM以外の部分(Reactが関与しない領域)は、通常のHTMLやJavaScriptで管理されるため、Reactの範囲が明確になる

state操作のルール

  1. 「setState メソッドを利用しないでStateの値を書き換えてはいけない」
  2. 「配列Stateを直接触ってはいけない」
    ※Reactはset関数に設定されたオブジェクトの同一性をチェックしているためミュータブルな操作をした場合、差分が検知されなくなる
  3. 「更新後のStateが更新前のStateの値に依存している場合(stateの値をset関数内で使用する場合)、setState メソッドには値ではなく関数を渡す」
    ※React はイベントハンドラの中のコードがすべて実行されるのを待ったうえで、状態を更新(コンポーネントを再レンダリング)するため

配列State更新時の注意点
・新しい配列でStateを更新する際に配列内にオブジェクトがある場合はシャローコピーされるため、更新対象のオブジェクトはクローンする(オブジェクトの参照自体を変える)

setSamples((samples) => {
      const newSamples = samples.map((sample) => {
        if (sample.id === id) {
	  // オブジェクトを展開して新しいオブジェクトを生成して返す
	  // プロパティを重複させると値が上書きされる
          return { ...sample, value };
        }
        return sample;
      });
      return newSamples;
    });

関数の命名規則
・イベントを処理する関数定義(イベントハンドラー)には handle[Event名]
・受け取る props には on[Event名]

propsの分割代入における懸念点
・親コンポーネントからの値かそのコンポーネント内で定義されたものかを瞬時に判断できないこと

TypeScript


createRoot
【注意点】
・引数にElement 型または DocumentFragment 型しかとれない
※「as」や「!(Non-null アサーション演算子)」を使用して引数の値を型アサーションする必要あり

document.getElementById
【注意点】
・戻り値の型はHTMLElement 型または Null
※取得予定の要素が存在しないケースがあり得るため

readOnly
【概要】
・オブジェクトの型定義の際にプロパティにReadOnlyを付与することでプロパティ値を読み取り専用とすることができる
参考)https://typescript-jp.gitbook.io/deep-dive/type-system/readonly

Eventごとの型
参考)https://zenn.dev/kenta0313/articles/a39fb1d8edc3a4

ジェネリクスの型引数に制約をつける
【背景】
・ジェネリクスを使用した関数の引数に対して、プロティ等を参照するとコンパイル時にエラーが発生する
→これはジェネリクスの型は任意の型を渡すことができるため、参照したプロパティがない場合があるから
【概要】
extendsキーワードを使用してジェネリクスの型に制約を設ける
→プロパティが存在すると保証され、コンパイルエラーは発生しない

<T extends Sample>

keyof型演算子
【概要】
オブジェクトの型からプロパティ名を型として返す演算子
【メリット】
プロパティ名のユニオン型を記述する必要がないため、オブジェクトのプロパティに修正・追加が起きた場合に修正する必要がなくなり、保守性が高まる

type Sample = {
  id: string;
  title: number;
};
type SampleKey = keyof Sample;
// 上と同じ意味
type SampleKey = "id" | "title";

インデックスアクセス型
【概要】
ブラケット記法を使って型のプロパティ値(型)を取得することができる

type Sample = { foo: number };
type Foo = Sample["foo"]; //number

type Person = { name: string; age: number };
type T = Person["name" | "age"]; //string | number

アンビエント宣言(declare宣言)
【概要】
・TypeScriptで型付けされていない型を定義する際に使用する
・アンビエント宣言によりTypeScriptコンパイラが型チェックを行えるようになる
【ユースケース】
・JavaScriptのプログラムをTypeScriptで扱う際に使用
【宣言の仕方】
・型定義ファイル(.d.ts)に記述を行う(型定義をモジュール化する)

declare type Sample = "checked";

メソッドの型注釈
以下2パターンの記述方法ができる

// パターン1
sample(a: string): string;
// パターン2(関数構文)
sample: (a: string) => string;

型ガード
【概要】
・変数を特定の型に絞り込む(厳密に型安全を保証する)場合に使用する
【ユースケース】
・APIからの戻り値でステートを更新する場合などで型ガードを行なってからステートの更新を行うようにする(型アサーションはプログラマ側で決めるため型安全は保証できない)
【使用例】

function isNumber(x: unknown): x is number {
  return typeof x === "number";
}