0
0

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 - The Complete Guide (incl. Next.js, Redux) Day 2

0
Last updated at Posted at 2026-06-28

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>

結果
image.png

① 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>

image.png

しかし、周りが黒くならないので 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) を隠蔽できる。

child-component
const Modal = React.forwardRef((props, ref) => {
  const dialogRef = React.useRef();

  React.useImperativeHandle(ref, () => ({
    open() {
      dialogRef.current.showModal();
    }
  }));

  return <dialog ref={dialogRef} />;
});
parent
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>
  • value propは必須
  • 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 と書いても同じ
  • 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 は便利だけどむずい

ここにまとめた

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?