復習のReact
基本的にJavaScriptで書くが、必要があればTypeScript版のコードを掲載する。
React18を対象にする
Next.jsなどのフレームワークは扱わない
Viteで環境構築する
コンポーネント
propsとstateはコンポーネントで値(状態 ≒ state)を扱う。propsは引数、stateはコンポーネントのローカル変数。
propsにはいろんな型の値を渡せる(プリミティブ & オブジェクト)。
const Child = (props) => {
console.log(props)
return (
<>
<p>id: {props.obj.id}</p>
<p>name: {props.obj.name}</p>
</>
)
}
const App = () => {
return (
<Child obj={{ id: 1, name: "kento" }} />
)
}
分割代入で受け取る。
const Child = ({ obj: { id, name }}) => {
return (
<>
<p>id: {id}</p>
<p>name: {name}</p>
</>
)
}
const App = () => {
return (
<Child obj={{ id: 1, name: "kento" }} />
)
}
TypeScriptでの型定義。
const Child = ({ obj: { id, name }}: { obj: { id: number, name: string }}) => {
return (
<>
<p>id: {id}</p>
<p>name: {name}</p>
</>
)
}
useState
const App = () => {
const [count, setCount] = useState(0)
return (
<>
<p>count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</>
)
}
type Book = {
id: number,
name: string
}
const books = [
{ id: 1, name: "kento" },
{ id: 2, name: "kouta" },
{ id: 3, name: "nao" },
{ id: 4, name: "miku" },
]
const Books = ({ books }: { books: Book[] }) => {
return (
<>
{books.map(({id, name}) => (
<Book id={id} name={name} key={id} />
))}
</>
)
}
const Book = ({ id, name }: Book) => {
return (
<>
<p>id: {id}, name: {name}</p>
</>
)
}
const App = () => {
return (
<Books books={books} />
)
}
stateの更新は非同期
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
};
このコードでは、setCount(count + 1) を2回呼び出しています。しかし、この場合、count は関数が呼ばれた時点の値 (すなわち現在の状態) を参照しています。
具体的な流れ 最初の setCount(count + 1) が呼ばれると、React は新しい状態を「現在の count + 1」に更新するようにスケジュールします。 2回目の setCount(count + 1) も、同じ現在の count を参照して「現在の count + 1」に更新するようにスケジュールします。 React は状態の更新をバッチ処理するため、結果として count は 1 増加するだけ になります。
const handleClick = () => {
setCount((count) => count + 1);
setCount((count) => count + 1);
};
このコードでは、setCount に アップデート関数 を渡しています。この関数は「前回の状態」を引数として受け取ります。
具体的な流れ 最初の setCount((count) => count + 1) が呼ばれると、React は現在の状態 (count) を基に「count + 1」を計算し、次の状態をスケジュールします。 2回目の setCount((count) => count + 1) が呼ばれると、React は 最初の更新後の状態 を引数として「count + 1」を計算し、次の状態をスケジュールします。 結果として、count は 2 増加 します。
違いの要点 コード1: count は関数が呼ばれた時点の値を直接参照しているため、複数の setCount 呼び出しは同じ値を基に処理されます。 コード2: count はアップデート関数の引数として渡される「最新の状態」を基に処理されるため、逐次的に更新されます。 推奨方法 状態更新が前回の状態に依存する場合は、コード2のようにアップデート関数を使用することを推奨します。これは、複数回の更新や非同期処理が絡む場合にも、意図通りの動作を保証できるからです。
コンポーネントが再レンダリングされるタイミング(詳しく書く)
- stateが変化した時
- propsが変化した時
- 親コンポーネントが再レンダリングされた時
key属性
children
ReactNodeで型定義?
参考 : Reactのchildrenの型で子コンポーネントを制御する(したかった)
useEffect
https://www.cxr-inc.com/blog/cc98228bc2ba48d3853d077f25fb831c
https://tech.enechange.co.jp/entry/2024/06/28/100239
https://developer.mamezou-tech.com/blogs/2024/08/13/react_useeffect/
https://zenn.dev/ippe/articles/a53386986ff236
https://zenn.dev/yumemi_inc/articles/react-effect-simply-explained
https://tech.iimon.co.jp/entry/2024/07/02/152657
Transition
Suspense
自動バッチング
複数のステート更新を一回にまとめる機能。
17と18で何が変わるかの例示。
flushSync
の説明は不要。
参考
>Vueユーザー「あれ、ReactでonClickが動かない。。」のワケ - Qiita
strictモード
【React】Strictモードの挙動【バージョン18による】 #JavaScript - Qiita
useState
https://zenn.dev/counterworks/articles/putting-props-to-use-state
自動バッチング
新機能:自動バッチング React 18の新機能Automatic Batchingを理解する|デザミス株式会社 U-motion 開発チーム
Suspense
https://tech.anotherworks.co.jp/article/react-suspense-react18