Closed54

Reactハンズオンラーニング読んだメモ

TatsukiTatsuki

2章 React学習に必要なJavaScriptの知識

TatsukiTatsuki

デストラクチャリング
オブジェクトを変数に代入したり引数として受け取る際に、必要なプロパティのみ取捨選択すること

こんな長い名前だったんだ

const sandwich = {
  bread: "dutch crunch",
  meat: "tuna",
  cheese: "swiss",
  toppings: ["lettuce", "tomato", "mustard"]
};

const { bread, meat } = sandwich;

console.log(bread, meat); // dutch crunch tuna”

TatsukiTatsuki

配列にも使えるらしい
filterかけた後の先頭要素だけ欲しいとかあるから、そういう時に使えそう

const [firstAnimal] = ["Horse", "Mouse", "Cat"]
console.log(firstAnimal) // Horce
TatsukiTatsuki

イミュータブルにすること意識すると、結構使える印象

const peaks = ["Test1", "Test2", "Test3"]
const revercePeaks = [...peaks].reverse()
TatsukiTatsuki

3章 Javascriptにおける関数型プログラミング

TatsukiTatsuki

第一級オブジェクト

あるプログラミング言語において、あるオブジェクトを変数に格納できるだけでなく、関数の引数として渡したり、戻り値として受け取ることができる場合、そのオブジェクトを第一級オブジェクトとして呼ぶ

知らなかった

TatsukiTatsuki

Javascriptは「関数」は第一級オブジェクト
本書では、Javascriptを関数型プログラミング言語と呼んでいる

TatsukiTatsuki

宣言型プログラミング
「何をするのか」{what)を記述することでアプリケーションを構築するスタイル

Javascriptはこうあるべき

TatsukiTatsuki

関数型プログラミングの基本概念

  • イミュータブル
  • 純粋関数
  • データの変換
  • 高階関数
  • 再帰
TatsukiTatsuki

イミュータブル

変異しない、変更を加えることが不可な状態

NG

let color_lawn = {
  title: "lawn",
  color: "#00FF00",
  rating: 0
}

function rateColor(color, rating) {
  color.rating = rating
  return color
}

console.log(rateColor(color_rawn, 5).rating) // 5
console.log(color_lawn.rating) // 5 元のオブジェクトにも影響が出ている

OK

let color_lawn = {
  title: "lawn",
  color: "#00FF00",
  rating: 0
}

const rateColor = (color, rating) => ({
  ...color,
  rating
})

console.log(rateColor(color_lawn, 5).rating) // 5
console.log(color_lawn.rating) // 0 元のオブジェクトに変更がない(破壊されていない)
TatsukiTatsuki

配列も同様
pushよく使っちゃうけど、関数型プログラミングの思考的にはNGらしい

NG

let list = [{ title: "Rad Red" }, { title: "Lawn" }, { title: "Party Pink" }]

const addColot = function(title, colors) {
  colors.puch({ title: title })
  return colors
}

console.log(addColor("Green").length) // 4
console.log(list.length) // 4 元の変数が更新されてしまっている

OK

let list = [{ title: "Rad Red" }, { title: "Lawn" }, { title: "Party Pink" }]

const addColor = (title, list) => [...list, { title }]

console.log(addColor("Green", list).length) // 4
console.log(list.length) // 3 元の変数が変更されていない
TatsukiTatsuki

純粋関数

純粋関数 : 引数の値のみを参照して、それをもとに計算し、値を返す関数
(少なくとも一つの引数を取り、値もしくは他の関数を戻り値として返す

NG
引数、戻り値も返却せず、関数外で宣言された変数に変更を加えているため副作用が生じている

const frededick = {
  name: "Frederick Douglass",
  canRead: false,
  canWrite: false
}

function selfEducate () {
  frededick.canRead: true,
  frededick.canWrite: true,
}

selfEducate()
console.log(frededick)
// {
//  name: "Frederick Douglass",
//  canRead: true,
//  canWrite: true
//  }

引数がイミュータブルではない

const frededick = {
  name: "Frederick Douglass",
  canRead: false,
  canWrite: false
}

const selfEducate = person => {
  person.canRead = true
  person.canWrite = true
  
  return person
}


// 引数が直接変更されている
console.log(selfEducate(frededick))
// {
//  name: "Frederick Douglass",
//  canRead: true,
//  canWrite: true
//  }
console.log(frededick)
// {
//  name: "Frederick Douglass",
//  canRead: true,
//  canWrite: true
//  }

ok

const frededick = {
  name: "Frederick Douglass",
  canRead: false,
  canWrite: false
}

const selfEducate = person => ({
  ...person,
  canRead: true,
  canWrite: true
})

console.log(selfEducate(frededick))
// {
//  name: "Frederick Douglass",
//  canRead: true,
//  canWrite: true
//  }
console.log(frededick)
// {
//  name: "Frederick Douglass",
//  canRead: false,
//  canWrite: false
//  }

TatsukiTatsuki

Javascriptで関数を書く際に意識すること

  1. 関数は少なくとも一つの引数を受け取らなければいけない (さらに関数は引数以外の値を参照してはいけない、グローバル変数の値によって振る舞いが変わる場合は純粋関数ではない)
  2. 関数は値もしくは他の関数を戻り値として返却しなければならない
  3. 関数は引数や関数外で定義された変数に直接変更を加えてはならない
TatsukiTatsuki

データの変換

データのイミュータブルである必要があり、データのコピーを作成して、コピーに変更を加えてどれを別の関数に渡しながら、最終的に変換されたデータを得ることができる

join: Arrayを結合する
filter: データの抽出
map: Arrayの各要素に対して処理を行い、元の配列と同サイズの配列を作成
reduce, reduceRight: 配列を単一の値に変換する

データの削除にはpopsliceを使用するのではなく、filterを使用するようにする

  const cutSchools = (cut, list) => list.filter((school) => school !== cut);
  console.log(cutSchools("Washington & Liberty", schools));
  // Yorktown, Wakefield

  console.log(schools.join("/n"));
  // Yorktown
  // Washington & Liberty
  // Wakefield

他参考
https://codesandbox.io/p/devbox/hxvrlw?migrateFrom=d2lm7t&layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cltja5ycg00063b6haw0rue8o%2522%252C%2522sizes%2522%253A%255B83.61079865016873%252C16.389201349831268%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cltja5ycg00023b6hv7i1z47v%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cltja5ycg00043b6huwi1oixf%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cltja5ycg00053b6hst6m7h6l%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B80.55127810286419%252C19.448721897135812%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cltja5ycg00023b6hv7i1z47v%2522%253A%257B%2522id%2522%253A%2522cltja5ycg00023b6hv7i1z47v%2522%252C%2522tabs%2522%253A%255B%255D%257D%252C%2522cltja5ycg00053b6hst6m7h6l%2522%253A%257B%2522id%2522%253A%2522cltja5ycg00053b6hst6m7h6l%2522%252C%2522activeTabId%2522%253A%2522cltja69he00b93b6hc85b5hkf%2522%252C%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522Development%2522%252C%2522port%2522%253A1234%252C%2522id%2522%253A%2522cltja69he00b93b6hc85b5hkf%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522path%2522%253A%2522%252F%2522%257D%255D%257D%252C%2522cltja5ycg00043b6huwi1oixf%2522%253A%257B%2522id%2522%253A%2522cltja5ycg00043b6huwi1oixf%2522%252C%2522activeTabId%2522%253A%2522cltja65lq00953b6hssgzqsio%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cltja5ycg00033b6hswom3cxy%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522cltja5yqr000hddf8c07dgebc%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522Development%2522%252C%2522id%2522%253A%2522cltja65lq00953b6hssgzqsio%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D

TatsukiTatsuki

高階関数

他の関数を引数に受け取るか、戻り値として関数を返す、またはそれら両方を満たす

TatsukiTatsuki

カリー化
その場で処理を実行せずに、関数として返却して、任意のタイミングで処理を実行することができる

  const userLogs = (userName) => (message) =>
    console.log(`${userName} -> ${message}`);
  const log = userLogs("john");

  getFakeMembers(20)
    .then((members) => log(`success: ${members.length} members`))
    .catch((error) => log(`error: ${error.message}`));
TatsukiTatsuki

4章 Reactの基本

TatsukiTatsuki

ブラウザでReactを動作させるためのライブラリ

React: ビューを構築するためのライブラリ (ビュー : UI)
ReactDOM: Reactで構築されたビューをブラウザで描画する

TatsukiTatsuki

React要素を作成するコード

React.createElement("h1", { id: "recipe-0" }, "Baked Salmon")

第一引数 : 作成したい要素のタイプ
第二引数 : 要素のプロパティ
第さん引数 : 子要素

上記コードは以下の要素に変換する

<h1 id="recipe-0">Baked Salmon</h1>
TatsukiTatsuki

ブラウザに描画する

const dish = React.createElement("h1", { id: "recipe-0" }, "Baked Salmon")
ReactDOM.render(dish, document.getElementById("root"))
TatsukiTatsuki

コンポーネント : 異なるデータでも同じ構成のデータで表示できるDOM

TatsukiTatsuki

コンポーネントの設計 : そのコンポーネントがスケールするかどうかを必ず考慮する

TatsukiTatsuki

Reactのコンポーネントの歴史
1: createClass
2: classコンポーネント

1は廃止済み
2は将来的に廃止される予定

そのため、これから作成するコンポーネントについては関数型コンポーネントとする

TatsukiTatsuki

5章 ReactとJSX

TatsukiTatsuki

JSX
= Javascript + XML

Javascriptのコードの中に、XMLのようなタグベースの構文を記述するための言語拡張

TatsukiTatsuki

Babelを活用して、JSXで書かれたJavascriptをブラウザが解釈できるコードにコンパイルする

TatsukiTatsuki

Reactでは2つ以上の要素をコンポーネントの戻り値として返すことはできない
=> 理由: Reactのコンポーネントは単一の要素ツリーを構築するように設計されているため

以下ChatGPT

Reactではコンポーネントが複数の要素を戻り値として直接返すことができない理由は、ReactのコンポーネントアーキテクチャとJSXの仕様に基づいています。Reactのコンポーネントは、単一の要素ツリーを構築するように設計されています。これは、コンポーネントが一つの要素を返す必要があるという制約につながります。その主な理由は以下の通りです。

1. **DOMツリーの整合性**: Reactは仮想DOMを使用して実際のDOMとの差分を計算し、最小限の更新を行います。コンポーネントが複数の要素を直接返すと、この仮想DOMツリーの構築と更新のプロセスが複雑になり、パフォーマンスの低下やバグの原因になり得ます。

2. **JSXの仕様**: JSXはReactでUIを宣言的に記述するための構文ですが、JavaScriptの関数と同様に、一つの式として評価される必要があります。複数の要素を含むJSXを返す場合、それらをグループ化して一つの式として扱う必要があります。

3. **コンポーネントの責務と再利用性**: Reactのコンポーネントは、独立して再利用可能なUIの単位として設計されています。コンポーネントが複数の要素を直接返すと、それらの要素の関係が不明瞭になり、コンポーネントの再利用性と保守性が低下します。

この制約に対処するために、Reactでは以下のいくつかの方法が提供されています。

- **フラグメント**: React Fragmentを使用すると、複数の要素をグループ化して返すことができますが、追加のDOM要素を挿入することなく、これらを一つの要素として扱うことができます。
  
- **配列**: 複数の要素を配列として返し、それぞれの要素に一意の`key`プロパティを割り当てることもできます。

これらの方法を使用することで、Reactのコンポーネントが複数の要素を返すときの制約を回避しつつ、整理されたDOMツリーを維持し、アプリケーションのパフォーマンスと保守性を向上させることができます。
TatsukiTatsuki

<></>は
<React.Flagment></React.Flagment>
の省略形のこと(初めて知った)

TatsukiTatsuki

webpack : モジュールバンドラ。異なる種類のファイル(js, cssなど)を単一のファイル(bundle: バンドル)に結合するためのツール

TatsukiTatsuki

6章 ステート管理

TatsukiTatsuki

制御されていないコンポーネント(uncontrolled component)
-> COMを介してデータにアクセスするコンポーネント

(uesRefを使ってinputの要素を取得する or valueを変更するなど)

import React, { useRef, type FormEvent } from "react";

// uncontrolled component
export default function AddColorForm({
	onNewColor,
}: { onNewColor: (title: string, color: string) => void }) {
	const txtTitle = useRef<HTMLInputElement | null>(null);
	const hexColor = useRef<HTMLInputElement | null>(null);

	const submit = (e: FormEvent<HTMLFormElement>) => {
		e.preventDefault();
		const title = txtTitle.current?.value ?? "";
		const color = hexColor.current?.value ?? "";
		onNewColor(title, color);
		if (txtTitle.current) {
			txtTitle.current.value = "";
		}
		if (hexColor.current) {
			hexColor.current.value = "";
		}
	};
	return (
		<form onSubmit={submit}>
			<input ref={txtTitle} type="text" placeholder="color title..." required />
			<input ref={hexColor} type="color" required />
			<button type="button">ADD</button>
		</form>
	);
}

TatsukiTatsuki

制御されたコンポーネント
 => 全てReactによって管理されている

import { useState } from "react";

export default function AddColorForm({
	onNewColor,
}: { onNewColor: (title: string, color: string) => void }) {
	const [title, setTitle] = useState<string>("");
	const [color, setColor] = useState<string>("#000000");

	const submit = (e: React.FormEvent<HTMLFormElement>) => {
		e.preventDefault();
		onNewColor(title, color);
		setTitle("");
		setColor("");
	};

	return (
		<form onSubmit={submit}>
			<input
				value={title}
				onChange={(event) => setTitle(event.target.value)}
				placeholder="color title..."
				required
			/>
			<input
				value={color}
				onChange={(event) => setColor(event.target.value)}
				required
			/>
			<button type="button">ADD</button>
		</form>
	);
}

TatsukiTatsuki

uuid、簡単に生成できるんだ
フロントで使うことは少なそうだけど、nodejsとかサーバーサイド側では使えそう

import { v4 } from "uuid";

const uuid = v4()
TatsukiTatsuki

こういうinputのカスタムhooksは結構使えそう
React hooks formでも使えないかな

import { useState } from "react";

export const useInput = (
	initialValue: string,
): [
	{ value: string; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void },
	() => void,
] => {
	const [value, setValue] = useState(initialValue);
	return [
		{
			value,
			onChange: (e: React.ChangeEvent<HTMLInputElement>) =>
				setValue(e.target.value),
		},
		() => setValue(initialValue),
	];
};

TatsukiTatsuki

この本でもPropsのバケツリレー問題について触れている
これの解消方法として、Contextを上げている

TatsukiTatsuki

コンテキストプロバイダー : データを格納する場所
コンテキストコンシューマー : データを取り出す場所

TatsukiTatsuki

これはglobalにstateを管理できるので、共通コンポーネント配下に入り込まないようにする必要がある

TatsukiTatsuki

こんな感じのArrayのstateを更新するメソッドの書き方は参考になるかも

const rateColor = (id: string, rating: number) => {
    setColors(
        colors.map((color) => (color.id === id ? { ...color, rating } : color)),
    );
};
TatsukiTatsuki

7章 フック

TatsukiTatsuki

コンポーネントの描画以外の処置については「副作用」となる
副作用となる処理については、useEffectに記載する

TatsukiTatsuki

useEffectは画面をレンダリングしてから、実行される

TatsukiTatsuki

これは使えそう

useEffectのコールバック関数に戻り値を設定することも可能
戻り値が別の関数だった場合、コンポーネントがツリーからアンマウントされた時に、その関数が実行される

TatsukiTatsuki

Javascriptの配列の比較は内容ではなく、同一インスタンスかどうかで判定する

const a = [1, 2, 3]
const b = [1, 2, 3]
console.log(a === b) // false


const c = d = [1, 2, 3]
console.log(c === d) // true
TatsukiTatsuki

メモ化
=> 情報工学の一般的な用語(知らなかった)。パフォーマンス改善のために計算結果をキャッシュすること

TatsukiTatsuki

useMemo, useCallback
コンポーネントが再レンダリングされるたびに、レンダリングや副作用の処理が実行されることを防ぐ

TatsukiTatsuki

配列であっても、useStateにセットされている場合については同一インスタンスとして見做している

TatsukiTatsuki

useLayoutEffect:

1: コンポーネントの描画関数がcall
2: useLayoutEffectの副作用関数がcall
3: ブラウザのpaint関数で、処理が実行
4: useEffectの副作用関数がcall

TatsukiTatsuki

useLayoutEffectの使い道:
描画前にブラウザのサイズから、コンポーネントのwidth, heightを計算する
マウスのポインターを追跡する

TatsukiTatsuki

useReducer
ステート更新のロジックを抽象化する

import { useEffect, useReducer } from "react";

function Checkbox() {
	// useStateからuseReducerに変更
	// const [checked, setChecked] = useState(false);
	const [checked, setChecked] = useReducer((checked) => !checked, false);

	useEffect(() => {
		alert(`checked: ${checked.toString()}`);
	});

	return (
		<>
			<input
				type={"checkbox"}
				value={checked.toString()}
				onChange={setChecked}
			/>
			{checked ? "checked" : "not checked"}
		</>
	);
}

TatsukiTatsuki

useReducerはobjectの一部を書き換える時とかにかなり使えそう
第二引数をPartialで宣言することがTypescriptで書くときのポイントになりそう

import { useReducer } from "react";

type UserType = {
	id: string;
	firstName: string;
	lastName: string;
	city: string;
	state: string;
	email: string;
	admin: boolean;
};

const firstUser: UserType = {
	id: "0391-3233-3201",
	firstName: "Bill",
	lastName: "Wilson",
	city: "Missoula",
	state: "Montana",
	email: "bwilson@mtnwilsons.com",
	admin: false,
};

export const User = () => {
	const [user, setUser] = useReducer(
		(user: UserType, newDetails: Partial<UserType>) => ({
			...user,
			...newDetails,
		}),
		firstUser,
	);

	return (
		<div>
			<h1>
				{user.firstName} {user.lastName}
			</h1>
			<p>
				{user.city}, {user.state}
			</p>
			<p>{user.email}</p>
			<p>{user.admin ? "Admin" : "Not Admin"}</p>
			<button
				onClick={() =>
					setUser({
						admin: true,
					})
				}
				type="button"
			>
				Make Admin
			</button>
		</div>
	);
};

このスクラップは4ヶ月前にクローズされました