udemyでreact学んでるメモ まだ135/440 くらい?先は長い・・
useRef
useRef を使うと html element そのものをobjectにできる。 refObject.current に現在のelementそのものが入る。elementが持つメソッドやattributeにそのままアクセスできる! ref = html element reference と覚えよう
import React from 'react';
function App() {
const fileInputRef = React.useRef();
function handleClick() {
fileInputRef.current.click(); // <-----
}
return (
<div id="app">
<p>Please select an image</p>
<p>
<input
ref={fileInputRef} // <-----
data-testid="file-picker"
type="file"
accept="image/*"
/>
<button onClick={handleClick}>Pick Image</button>
</p>
</div>
);
}
export default App;
useRef は値が変わったときに再レンダリングしない
Reactは
currentの中身が変わったことを監視しない
ので再描画しません。
- 変更すると画面を更新したい -> useState
- 「画面とは関係ないデータ」/* 変更しても画面更新不要 -> useRef
useStateは「UIの状態」を管理するもの、useRefは「再レンダリング不要な値やDOMへの参照」を保持するものです。
componentの外の変数は共有される
componentを複数再利用するとき、再レンダリングを防ぐためにcomponentの外に変数を置くとする。こうすると、全てのcomponentで共通の変数が使われてしまう。
import { useState } from 'react';
let timer; <---
export default function TimerChallenge({ title, targetTime }) {
そもそも componentの中にある変数や関数を書くかどうかは、再レンダリングでその行を実行させたいかどうか
<dialog> tag なんてあったのか
HTML5で(仕様化され、主要ブラウザで広く使えるようになったのは比較的最近)、モーダルダイアログ専用の要素として が追加されました。
<dialog open>
<p>Hello!</p>
</dialog>
① open 属性
単に「表示されている状態」にする属性です。表示はされますが、
- 背景は暗くならない
- 外側も普通にクリックできる
- モーダルではない
dialog.show(); // モデルレスで表示
dialog.showModal(); // モーダルで表示
dialog.close(); // 閉じる
showModal の特徴は
- 背景が暗くなる
- 外側をクリックできない
- Escで閉じられる(既定)
- ::backdrop が表示される
- フォーカスがダイアログ内に移る
<form method="dialog"> で閉じれる
htmlだけ、これだけでdialogを閉じれる
<dialog open>
<p>Hello!</p>
<form method="dialog">
<button>CLOSE!</button>
</form>
</dialog>
しかし、周りが黒くならないので showModal() を使うのが普通
forwardRef は古い書き方
React 18以前(現在のUdemy課題)
const Input = React.forwardRef(function Input(props, ref) {
return <input ref={ref} />;
});
React 19
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
- React 19: 2024年末に正式リリース
- Next.js 16: 2025年10月に正式リリース。React 19.2系をサポートしています。
今(2026年)なら
- React 18 ← 少し前だけど現役で大量にある
- React 19 ← 新しい標準
- Next.js 15 ← まだ多い
- Next.js 16 ← 最新
useImperativeHandle
useRefで dialog.current.showModal() はできるけど、child componentの中身が <dialog> じゃなくて例えば <input> になったら showModal() メソッドがなくなってしまう。child componentの変更でparent componentも影響を受ける。これを防ぐのが useImperativeHandle.
↓の例では 親から来たrefで <dialog> elementを使わせず、代わりに dialog.current に open() methodを追加している。これを親から呼ぶことで、子供の中の変更(e.g. dialog -> input) を隠蔽できる。
const Modal = React.forwardRef((props, ref) => {
const dialogRef = React.useRef();
React.useImperativeHandle(ref, () => ({
open() {
dialogRef.current.showModal();
}
}));
return <dialog ref={dialogRef} />;
});
const modalRef = React.useRef();
<Modal ref={modalRef} />
<button
onClick={() => modalRef.current.open()}
>
Open
</button>
useImperativeHandle は useRef とセットで使われる兄弟、と覚えよう。
useRef
↓
「値やDOMへの参照を持つ」
useImperativeHandle
↓
「その参照を親にどう見せるか決める」
useRef
↓
実物を持つ
useImperativeHandle
↓
実物をラップして
親向けAPIを公開する
Declarative(宣言的) vs Imperative(命令的)
Declarative(宣言的)
{isOpen && <Modal />}
「どう表示するか」はReactに任せる。
Imperative(命令的)
modal.open();
modal.close();
input.focus();
「これを実行しろ」と命令する。
useImperativeHandle の Imperative はこの「命令的」の意味です。
つまり
親が命令できる API を作る Hook
という名前なんです。
createPortal でdomをテレポート
modalとセットで使うやつ
普通のchild componentで <dialog> を使うとparent componentの中にDOMができる。でもmodalは外に出したい(component cssの影響を受けやすくなるので)
<body>
<div id="modal"></div> <--- こっちにmodalは出したい
<div id="content">
... <--- こっちの下じゃなくて
とはいえ、componentは <div id="content"> の下に作っていうから、そうはできない。そんな時は modal componentだけを <div id="modal"> の方にテレポートする。
import { createPortal } from "react-dom";
export default function Modal({ children }) {
return createPortal(
<div className="modal">
{children}
</div>,
document.getElementById("modal") // <---
);
}
こうすると、実際のDOMがあら不思議、<div id="modal"> の中に表示される。
createPortal いつ使う?
代表例は次のような「画面全体に重ねて表示したいUI」です。
- モーダル(最も一般的)
- ダイアログ
- ポップアップ
- ツールチップ
- ドロップダウンメニュー
- コンテキストメニュー
- トースト通知
これらは親要素の overflow: hidden や z-index の影響を受けにくくするために、body 直下などへ Portal を使って描画することがよくあります。
Contextって何がいいの。stateと何が違うの
ContextとStateの違い
一言でいうと、
Stateは「値そのもの」、Contextは「そのStateを遠くのコンポーネントへ共有する仕組み」。
ContextはStateの代わりではなく、Stateを運ぶための仕組み。
Stateだけの場合
親コンポーネントがStateを持つ。
function App() {
const [theme, setTheme] = useState('light');
return <Page theme={theme} />;
}
コンポーネント構成が
App
├─ Header
└─ Page
└─ Button
だった場合、Buttonで theme を使いたいだけなのに、
App
↓
Page
↓
Content
↓
Button
のように途中のコンポーネントを経由して
<Page theme={theme} />
<Content theme={theme} />
<Button theme={theme} />
と延々 props を渡す必要がある。
これを Props Drilling という。
Contextを使う場合
Stateは親が持ったまま。
const [theme, setTheme] = useState('light');
そのStateをContextで共有する。
App
│
│ Provider
▼
Header ← useContext()
Page ← useContext()
Button ← useContext()
必要なコンポーネントは直接取得できる。
const { theme } = useContext(ThemeContext);
途中のコンポーネントに props を渡す必要がない。
StateとContextの役割
| State | Context |
|---|---|
| データを保持する | データを共有する |
useState() |
createContext() + Provider
|
| コンポーネント単位で管理 | Provider配下へ配布 |
実際は組み合わせて使う
Contextだけでは状態は持てない。
一般的には、StateをContextで共有する。
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider
value={{
theme,
toggleTheme: () => {
setTheme(prev =>
prev === 'light' ? 'dark' : 'light'
);
}
}}
>
{children}
</ThemeContext.Provider>
);
}
つまり
- State が値を管理する
- Context がその値を共有する
という役割分担になっている。
Contextは「グローバル変数」ではない
Contextは Provider配下だけ で共有される。
<App>
<ThemeProvider>
← この中だけ共有される
</ThemeProvider>
</App>
Providerの外では、そのContextの値は取得できない。
まとめ
- State = データを保持する
- Context = データを共有する
- ContextはStateの代わりではない
- ContextはProps Drillingを解消するための仕組み
- 一般的には「State + Context」を組み合わせて使う
# React Context (`createContext`, `Provider`, `useContext`, `use`)
React Contextは、**propsを何段階も渡さずに状態や関数を共有する仕組み**。
---
# 1. Contextを作る
```jsx
import { createContext } from 'react';
export const CartContext = createContext({
items: [],
});
createContext() の引数はデフォルト値。
通常は Provider が値を上書きするため、このデフォルト値が使われることはほとんどない。
2. Provider(値を配る)
Contextを利用するには、上位で Provider を使って値を配る。
React 18以前(Udemy教材はこちら)
<CartContext.Provider
value={{
items: shoppingCart.items,
}}
>
<Header />
</CartContext.Provider>
-
valuepropは必須 -
valueに渡した値が、子孫コンポーネントから取得できる - React 18以前では
Context.Providerを使う
React 19以降
React 19では Context自体がProviderとして振る舞える ようになったため、.Provider を省略できる。
<CartContext
value={{
items: shoppingCart.items,
}}
>
<Header />
</CartContext>
これは次と完全に同じ意味。
<CartContext.Provider
value={{
items: shoppingCart.items,
}}
>
<Header />
</CartContext.Provider>
.Provider が廃止されたわけではなく、省略記法(シンタックスシュガー)が追加されただけ。
UdemyなどReact 18前提の教材では
Context.Providerを使う。
3. Contextを読む
Contextの値を受け取る方法は複数ある。
useContext()(React 16.8〜)
現在もっとも一般的な方法。
import { useContext } from 'react';
const cartContext = useContext(CartContext);
分割代入もできる。
const { items } = useContext(CartContext);
Contextの値が変わると、そのContextを読んでいるコンポーネントは再実行(re-render)される。
use()(React 19〜)
React 19では use() でもContextを読める。
import { use } from 'react';
const cartContext = use(CartContext);
use() はContextだけでなく Promise も扱える。
useContext() と use() の違い
useContext()
- Context専用
- Hookなので通常のHookルールに従う
- 条件分岐・ループ・ネスト内では呼べない
if (cond) {
useContext(CartContext); // ❌
}
use()
- ContextもPromiseも読める
- Contextを読む場合のみ、条件分岐やループ内でも呼べる
if (cond) {
use(CartContext); // ✅
}
React 19以降なら
- Contextだけ読む →
useContext()/use()のどちらでもOK - Promiseも扱う →
use() - Server Componentsを積極的に使う →
use()
React Contextの全体像
React Contextには「値を配る側」と「値を受け取る側」がある。
React 18以前
ThemeContext
│
├── Provider(値を配る)
│
└── 子コンポーネント
├── Consumer (昔)
└── useContext() (現在)
React 19以降
ThemeContext (= Providerとしても使える)
│
└── 子コンポーネント
├── Consumer
├── useContext()
└── use()
Provider
Contextの値を配るコンポーネント。
React 18以前
<ThemeContext.Provider value={contextValue}>
<Page />
</ThemeContext.Provider>
React 19以降
<ThemeContext value={contextValue}>
<Page />
</ThemeContext>
どちらも意味は同じ。
Consumer(昔の受け取り方)
Hooks登場前の書き方。
<ThemeContext.Consumer>
{(context) => (
<button onClick={context.toggleTheme}>
Toggle Theme
</button>
)}
</ThemeContext.Consumer>
現在でも使えるが、関数コンポーネントではほとんど使われない。
useContext()(現在の標準)
const themeCtx = useContext(ThemeContext);
return (
<button onClick={themeCtx.toggleTheme}>
Toggle Theme
</button>
);
Consumer と同じ役割を、よりシンプルに実現できる。
まとめ
-
createContext():Contextを作る -
Provider:Contextの値を配る- React 18以前:
Context.Provider - React 19以降:
Contextと書いても同じ
- React 18以前:
-
Consumer:昔の受け取り方 -
useContext():現在の標準的な受け取り方 -
use():React 19で追加。ContextもPromiseも扱える - Contextの値が変わると、そのContextを利用しているコンポーネントは再実行(re-render)される
navigator で取れる情報
navigator はブラウザ/デバイスの情報を持つグローバルオブジェクト。window・document などと同じくブラウザが自動で提供するため import 不要。
よく使うもの
| プロパティ / メソッド | 取れる情報 |
|---|---|
navigator.geolocation |
位置情報(緯度・経度)※ユーザー許可が必要 |
navigator.language / navigator.languages
|
ブラウザの言語設定(例 "ja", ["ja", "en-US"]) |
navigator.onLine |
オンライン / オフライン状態(true / false) |
navigator.userAgent |
ブラウザ・OSの識別文字列 |
navigator.clipboard |
クリップボード読み書き(writeText など) |
navigator.cookieEnabled |
Cookie が有効かどうか |
デバイス系
| プロパティ / メソッド | 取れる情報 |
|---|---|
navigator.hardwareConcurrency |
CPU の論理コア数 |
navigator.deviceMemory |
端末のメモリ量(GB, おおよそ) |
navigator.maxTouchPoints |
タッチ対応の同時タッチ数(0ならタッチ非対応) |
navigator.mediaDevices |
カメラ・マイクへのアクセス(getUserMedia) |
navigator.vibrate(ms) |
スマホのバイブレーション |
navigator.getBattery() |
バッテリー残量・充電状態 |
その他
| プロパティ / メソッド | 取れる情報 |
|---|---|
navigator.connection |
ネットワーク種別・速度(effectiveType = "4g" など) |
navigator.permissions |
各種権限の状態確認 |
navigator.serviceWorker |
Service Worker(PWA・オフライン対応) |
navigator.sendBeacon() |
ページ離脱時にデータ送信(分析用など) |
navigator.share() |
OS のネイティブ共有シート呼び出し |
試す例
console.log(navigator.language); // "ja"
console.log(navigator.onLine); // true
console.log(navigator.hardwareConcurrency); // 8
console.log(navigator.userAgent); // "Mozilla/5.0 ..."
console.log(navigator.maxTouchPoints); // 0 (PC) / 5 (スマホ)
注意
geolocation・clipboard・mediaDevices・getBattery など プライバシーに関わるもの は、ユーザーの許可(パーミッション)や HTTPS 環境が必要。
Side Effects
useEffectはuseStateの無限ループを防止するために使う
ここにまとめた。
useEffect は処理を render 後に走らせる仕組み。
React の1回の render は「Render phase(component 関数を呼んで次の画面を計算する)」と「Commit phase(計算結果を DOM に反映する)」に分かれている。useEffect の処理が走るのは Commit のさらに後(Passive Effects)。
https://qiita.com/uturned0/items/9bcdcedc3a0a5c2c1083
https://qiita.com/uturned0/items/9f44d57d1805bbfe8b66
useState が走った後に useEffect で「予約」がされる。renderが終わったあとに実行される。
無限ループする例
useEffect(() => {
setCount(count + 1); <--- 値を変える
}, [count]); <--- depsが変える値だから無限ループ
この時の動き
count=0
↓
effect実行
↓
setCount(1)
↓
stateが変わる予約 がされたので再レンダリング <---
↓
useState が新しい count=1 を返す
↓
effect実行
↓
setCount(2)
...
無限ループ防止方法
useEffect
useEffect(() => {
setCount(1);
}, []); // deps = 空配列 → 最初の1回だけ
💡 useEffect の deps に [] を使う理由
-
depsは「依存関係の配列」。 -
[]= 依存が0個なので、初回のみ実行。-
[]は「比較対象が0個の依存配列」を表す、一貫した設計。
-
-
undefined(省略) = 毎回実行。 -
[count]=countが変わったときだけ実行。 -
nullはdepsの type として無効(配列 | undefined ではない)。 - React は前回と今回の配列を
Object.isで比較して実行可否を決める。
2. 依存する値を正しく指定する
useEffect(() => {
console.log('countが変わったときだけ実行');
}, [count]); // count が変わったときだけ実行
3. 関数型アップデートで不要な依存を避ける
useEffect(() => {
const timer = setInterval(() => {
setCount(prevCount => prevCount + 1); // prevを使えば count を依存に入れなくてよい
}, 1000);
return () => clearInterval(timer); // クリーンアップも忘れずに
}, []);
depsがあっても初回は必ず走る
一生変わらない値があっても一度は必ず走る
const open = true;
useEffect(() => {
...
}, [open]);
DOMが消える時に useEffect をお掃除 発火させる
useEffect の帰り値に関数を渡すとDOMが死ぬ時に実行される
useEffect(() => {
const timer = setTimeout(() => {
onConfirm();
}, 3000);
return () => clearTimeout(timer); // <-----
}, [open]);
初回は強制実行 -> openが変わったら実行 -> timer中にdomが消えたらclearTimeoutされる
useCallback
useCallback が欲しい理由:useEffect deps に関数を入れるな
Javascriptでは関数はオブジェクト。 関数objectはrenderingされるたびに作り直される! だから毎回発火してしまう! 🔥
useEffect(() => {
...
}, [myFunc]); // <-----
useCallback を使うと関数objectが再生成されない。
const handleRemovePlace = useCallback(
function handleRemovePlace() {
...
}
}, []);
depsはuseEffectと同じ。depsが変わったときに関数objectが再生成される。
useCallback の最も多い用途の1つがパフォーマンス最適化です。
だろうね、
key は便利だけどむずい
ここにまとめた

