ブログ

  • fetchを使ったAPI通信(Next.js)その①

    本章よりfetchを使ったAPI通信について学んでいきます。

    API通信とは

    API(Application Programming Interface)通信とは、アプリ同士がデータをやり取りする仕組みのことです。

    Next.jsはフロントエンドアプリの構築に使用し、DBとやりとりするバックエンドは別の言語などでアプリを作り、相互に通信することで一つのアプリケーションを構築することが一般的です。

    Next.jsとバックエンドのアプリの通信のことをAPI通信と呼びます。

    JSONとは?

    JSON(JavaScript Object Notation)とはデータを「軽量でシンプルな形式」で表現するためのフォーマットです。

    (例:

    [
      { "id": 1, "name": "チワワ", "age": 2 },
      { "id": 2, "name": "シバイヌ", "age": 4 },
      { "id": 3, "name": "ゴールデンレトリバー", "age": 5 }
    ]

    このようにJavascriptのオブジェクトのような記述になっています。

    Next.jsのAPI通信は下記の理由からこのJSON形式のデータで行います。

    1. テキストデータなので、サイズが小さく通信が速い
    2. ほぼすべてのプログラミング言語で使える
    3. fetch で簡単に送受信できる
    4. Next.js の API ルートと相性がいい

    fetchとは?

    fetch JavaScript の組み込み関数で、API からデータを取得したり、データを送信するのに使うもの

    使い方を見ていくために実際にコードを書いていきましょう。

    アプリ作成+DB構築

    まずアプリを作成します。

    npx create-next-app@latest api-app

    今回はjson-serverというローカルで簡易的に構築できるAPIサーバーを作成し、DBとして扱います。

    json-serverをインストール

    npm install json-server

    プロジェクトのルートにdb.jsonを作成

    db.json

    {
        "dogs": [
            {
                "id": 1,
                "name": "チワワ",
                "age": 2
            },
            {
                "id": 2,
                "name": "シバイヌ",
                "age": 4
            },
            {
                "id": 3,
                "name": "ゴールデンレトリバー",
                "age": 5
            }
        ]
    }

    次に、json-serverを起動します。

    npx json-server --watch db.json --port 3001

    これでhttp://localhost:3001/dogs にアクセスするとデータが取得できます。

    ページ作成

    次に、page.jsに下記記述をします。

    app/page.js

    "use client";
    import { useState } from "react";
    import ReadDogs from "./components/ReadDogs";
    import CreateDog from "./components/CreateDog";
    import UpdateDog from "./components/UpdateDog";
    import DeleteDog from "./components/DeleteDog";
    
    export default function Home() {
      const [dogs, setDogs] = useState([]);
    
      return (
        <div style={{ padding: "20px" }}>
          <ReadDogs dogs={dogs} setDogs={setDogs} />
          <CreateDog dogs={dogs} setDogs={setDogs} />
          <UpdateDog dogs={dogs} setDogs={setDogs} />
          <DeleteDog dogs={dogs} setDogs={setDogs} />
        </div>
      );
    }

    useStateでdogsと更新用関数setDogsを作成し、

    • データ取得(Read)
    • データ追加(Create)
    • データ更新(Update)
    • データ削除(Delete)

    用のそれぞれのコンポーネントにpropsとして渡しています。

    次回から順番に各コンポーネントを作成していきます。

  • Reace Hooks その⑦ 発展編(任意)

    useReducerとは?

    • useState よりちょっと賢い「状態管理の方法」
    • 「どう変えるか?」のルールを reducer という関数にまとめて管理する
    • 状態が増えてもスッキリ書ける🐾

    useReducerの使い方

    少し複雑ですので、まず実際のコードで見ていきます。

    まずはuseReducerを使わない、カウンター

    app/components/CounterState.js

    import { useState } from "react";
    
    function CounterState() {
        const [count, setCount] = useState(0);
    
        return (
            <div>
                <p>カウント: {count}</p>
                <button onClick={() => setCount(count + 1)}>+1</button>
                <button onClick={() => setCount(count - 1)}>-1</button>
            </div>
        );
    }
    
    export default CounterState;

    次にuseReducerを使ったカウンター

    app/components/CounterReducer.js

    "use client";
    
    import { useReducer } from "react";
    
    // ✅ 1. ルール(reducer)を作る
    function reducer(state, action) {
        if (action.type === "increment") return { count: state.count + 1 };
        if (action.type === "decrement") return { count: state.count - 1 };
        return state;
    }
    
    function CounterReducer() {
        // ✅ 2. `useReducer` を使う
        const [state, dispatch] = useReducer(reducer, { count: 0 });
    
        return (
            <div>
                <p>カウント: {state.count}</p>
                {/* ✅ 3. ルールに従って変更を指示する */}
                <button onClick={() => dispatch({ type: "increment" })}>+1</button>
                <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
            </div>
        );
    }
    
    export default CounterReducer;

    コンポーネントを読み込んで表示します。

    app/page.js

    import CounterState from "./components/CounterState";
    import CounterReducer from "./components/CounterReducer";
    
     <CounterState />
     <CounterReducer />

    下図のように同じ動きをするカウンターが表示されています。

    コード解説

    順番が前後しますが、下記の部分でuseReducerを設定しています。

    const [state, dispatch] = useReducer(reducer, { count: 0 });

    これは下図のようになっています。

    書き方のほとんどがuseStateと同じですが、「reducer」という「ルールを定めた関数」が付け足されています。

    reducerはこの部分で定義されています。

    function reducer(state, action) {
        if (action.type === "increment") return { count: state.count + 1 };
        if (action.type === "decrement") return { count: state.count - 1 };
        return state;
    }

    渡ってきた「type」の値によって処理を分岐しています。

    ここではわかりいやすいようにif分で分岐していますが、一般的にはswitch分で記述します。

    (:例文

    function reducer(state, action) {
        switch (action.type) {
            case "increment":
                return { count: state.count + 1 };
            case "decrement":
                return { count: state.count - 1 };
            default:
                return state;
        }
    }

    JSXではボタンクリックでそれぞれのtypeの値に応じた更新用関数が動きます。

     <button onClick={() => dispatch({ type: "increment" })}>+1</button>
     <button onClick={() => dispatch({ type: "decrement" })}>-1</button>

    このように「ルールを定めた関数」に一つのstateに対して異なる動きをする更新用関数をまとめて記述できるのが特徴です。

    また、reducer,state,action,dispatch,typeの関数名や値の名前は自由に記述できます。

    但し、上記のように記述するのが一般的なので、「useReducerの公式」という風に捉えて、一連の書き方と使い方をセットで覚えておいてください。

    React Hooks まとめ

    これまで学習してきたHooksの特徴と使い所をまとめました。

    Hook特徴(できること)使い所(いつ使う?)
    useState✅ コンポーネントの状態(データ)を管理
    setState で値を更新
    🔹 カウンターや入力フォームの状態管理
    🔹 ボタンのON/OFFやトグルの管理
    🔹 API送受信用のデータ管理
    useEffect✅ 副作用(データ取得・イベント登録)を管理
    ✅ コンポーネントの「マウント」「更新」「アンマウント」に対応
    🔹 API からのデータの送受信
    🔹 イベントリスナーを登録・削除するとき
    🔹 ページのタイトルを変更するとき
    useRef✅ DOM の参照を取得して操作
    ✅ レンダリングなしでデータを保持
    🔹 inputvideo にフォーカスを当てる
    🔹 スクロール位置を取得・管理する
    🔹 カウントや値を更新しても再レンダリングさせたくないとき
    useContext✅ コンポーネント間でデータを簡単に共有
    props のバケツリレーを解消
    🔹 テーマ(ダークモード・ライトモード)の管理
    🔹 ログイン情報の管理(ユーザー名・メールアドレス)
    🔹 言語設定(i18n)をアプリ全体で共有
    useReduceruseState よりも複雑な状態管理を整理
    ✅ 状態の変更ルールを reducer にまとめる
    🔹 カートやフォームの状態管理(追加・削除・リセット)
    🔹 ログインの状態管理(ログイン・ログアウト・トグル)
    🔹 useState では管理が複雑になりそうなとき

  • Reace Hooks その⑦ 発展編(任意)

    これまで、React HooksのuseState,useEffect,useRefについて学んできました。

    ここからは発展編ということで、必須ではありませんが、知っておくと便利なHooks(useContext,useReducer)について触れていきます。

    💡useContextとは?

    useContextは、「親コンポーネントから子コンポーネントにデータを渡すのをラクにする仕組み」 です。

    普通、データを渡すには props を使いますが、階層が深くなるとめちゃくちゃ面倒になります

    💩 「props 地獄」を解決するのが useContext

    useContextの使い方

    実際にコードを書いて動きを見ていきましょう。

    まずアプリを作成します。

    npx create-next-app@latest use-context-app

    次に、useContextを定義するファイルを作成します。

    app/UserContext.js

    import { createContext } from "react";
    
    // ✅ ユーザー情報を管理する Context を作成
    export const UserContext = createContext();

    そして、孫、子、親コンポーネントを作成していきます。

    app/components/GrandChild.js

    "use client";
    
    import { useContext } from "react";
    import { UserContext } from "../UserContext";
    
    function GrandChild() {
        const { user } = useContext(UserContext); // ✅ Context からユーザー情報を取得
    
        return (
            <div style={{ border: "2px solid blue", padding: "10px", marginTop: "10px" }}>
                <h3>👶 孫コンポーネント</h3>
                <p><strong>ユーザー名:</strong> {user.name}</p>
                <p><strong>メールアドレス:</strong> {user.email}</p>
            </div>
        );
    }
    
    export default GrandChild;

    app/components/Child.js

    "use client";
    
    import { useContext } from "react";
    import { UserContext } from "../UserContext"; // ✅ Context をインポート
    import GrandChild from "./GrandChild"; // ✅ 孫コンポーネントをインポート
    
    function Child() {
        const { user } = useContext(UserContext); // ✅ Context からユーザー情報を取得
    
        return (
            <div style={{ border: "2px solid green", padding: "10px" }}>
                <h2>👦 子コンポーネント</h2>
                <p><strong>ユーザー名:</strong> {user.name}</p>
                <p><strong>メールアドレス:</strong> {user.email}</p>
                <GrandChild /> {/* ✅ 孫コンポーネントを表示 */}
            </div>
        );
    }
    
    export default Child;

    app/components/App.js

    "use client";
    
    import { useState } from "react";
    import { UserContext } from "../UserContext"; // ✅ Context をインポート
    import Child from "./Child"; // ✅ 子コンポーネントをインポート
    
    function App() {
        const [user, setUser] = useState({
            name: "しょんべん太郎",
            email: "shonben@example.com",
        });
    
        return (
            // ✅ `UserContext.Provider` で `user` を全コンポーネントに提供
            <UserContext.Provider value={{ user, setUser }}>
                <div style={{ border: "2px solid red", padding: "10px" }}>
                    <h1>👨‍💻 親コンポーネント(App)</h1>
                    <p><strong>ユーザー名:</strong> {user.name}</p>
                    <p><strong>メールアドレス:</strong> {user.email}</p>
                    <Child />
                </div>
            </UserContext.Provider>
        );
    }
    
    export default App;

    最後にpage.jsで読み込みます。

    app/page.js

    import Image from "next/image";
    
    import App from "./components/App";
    
    export default function Home() {
      return (
        <App />
      );
    }

    開発サーバーを立ち上げて画面を確認します。

    npm run dev

    このように親コンポーネントで定義した値をそれぞれのコンポーネントで継承し表示しています。

    コード解説

    まずUserContext.jsですが、

    app/UserContext.js

    export const UserContext = createContext();

    このようにUserContextというContextを作成しています。

    またexportの後に「default」をつけていませんが、コンポーネントなどの関数の宣言(function)」は export default でエクスポートできますが、今回のように定数の宣言の場合は「default」がつけられません

    次に、親コンポーネントの主要な部分を説明します。

    app/components/App.js

    import { UserContext } from "../UserContext"; // ✅ Context をインポート

    UserContext.jsからUserConxetをインポートしています。

    このように、「default」をつけずにエクスポートされたものは{}をつけて且つエクスポート元で定義した名前をそのまま記述する必要があります。

    逆に、「default」をつけたエクスポートした関数はインポート先では別の名前でインポートすることもできます。

    <UserContext.Provider value={{ user, setUser }}>

    親コンポーネントではこのように渡したい値を「Provider」と呼ばれるコンポーネントのvalueプロパティに設定します。

    valueの値が{{}}と二重括弧になっていますが、内側の{}はuser、setUserと複数の値を渡すためにオブジェクトとして渡すための{}です。

    渡す値が1つの場合は{}は1重で大丈夫です。

    次に子コンポーネントを見ます。

    app/components/Child.js

    import { UserContext } from "../UserContext"; 

    子コンポーネントでもUserContext.jsからUserContextをインポートします。

     const { user } = useContext(UserContext);

    ここで親コンポーネントで設定された値をuseContextを使って受取り、{user}オブジェクトに代入しています。

    ここでも、渡ってくる値が1つの場合は、{}をつけてオブジェクトにする必要はありません。

     <p><strong>ユーザー名:</strong> {user.name}</p>
     <p><strong>メールアドレス:</strong> {user.email}</p>

    ここで親コンポーネントで定義したuserオブジェクトの値をそれぞれ表示しています。

    userオブジェクト(app/components/App.js)

    {
       name: "しょんべん太郎",
       email: "shonben@example.com",
     }

    最後に孫コンポーネントを見ていきます。

    app/components/GrandChild.js

    import { UserContext } from "../UserContext";
    const { user } = useContext(UserContext);
     <p><strong>ユーザー名:</strong> {user.name}</p>
     <p><strong>メールアドレス:</strong> {user.email}</p>

    このように、孫コンポーネントと子コンポーネントが全く同じ書き方になっています。

    useContextでどこからでもデータにアクセスできる

    propsでのデータ継承のように、親→子→孫とバケツリレーをしなくても、例えば子コンポーネントでは継承せずに、孫コンポーネントのみでも扱えたりします。

    つまり、useContextを使うことで、

    親コンポーネントで渡したデータにどこからでもアクセスできる

    というわけです。

    なので、子コンポーネントと孫コンポーネントで同じ書き方でも継承できたのです。

    useContextを使うケース

    ここで見てきたユーザー情報=ログイン情報ようなグローバルで扱いたいデータ向けに使用します。

  • Reace Hooks その⑥

    useRefとは?

    useRef は React の フック(Hook) の一つで、次のような用途で使われます。

    ① コンポーネントが再レンダリングされても値を保持するstate とは違い、変更しても再レンダリングしない)
    ② DOM(HTML要素)を直接操作するdocument.querySelector() のような処理を React で実装)

    useRefの使い方

    実際にコードを書いて見ていきましょう。

    アプリ「useref-app」を作成しFocusInputコンポーネントを作ります。

    npx create-next-app@latest useref-app

    app/components/focusInput.js

    "use client";
    
    import { useEffect, useRef, useState } from "react";
    
    function FocusInput() {
        const inputRef = useRef(null);
        const [count, setCount] = useState(0);
    
        // ✅ コンポーネントがマウントまたは再レンダリングされるたびに alert を表示
        useEffect(() => {
            alert("コンポーネントがレンダリングされました!");
        });
    
        const handleFocus = () => {
            inputRef.current.focus();
            inputRef.current.value = "フォーカスされました!"; // ✅ `inputRef` を変更
        };
    
        return (
            <div>
                <input ref={inputRef} type="text" placeholder="Type here..." />
                <button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>
                <button onClick={() => setCount(count + 1)}>カウント増加(useState変更)</button>
                <p>カウント: {count}</p>
            </div>
        );
    }
    
    export default FocusInput;

    app/page.js

    import Image from "next/image";
    import FocusInput from "./components/focusInput";
    
    export default function Home() {
      return (
        <FocusInput />
      );
    }
    

    開発サーバーを作り、画面を確認します。

    npm run dev

    最初に「コンポーネントがレンダリングされました!」とアラートが表示されます。

    次に「フォーカスを当てる」をクリックしてもアラートは出ません。

    「カウント増加」をクリックすると

    アラートが出ます。

    このようにuseStateは更新されるごとにコンポーネントが再レンダリングされるのに対して、useRefは更新しても再レンダリングされないことがわかります。

    コード解説

    コードを解説していきます。

    クライアントコンポーネントの宣言、必要な機能のインポートをしています。

    "use client";
    
    import { useEffect, useRef, useState } from "react";

    使用するstateとrefを設定しています。

    function FocusInput() {
        const inputRef = useRef(null);
        const [count, setCount] = useState(0);

    useEffectを使ってレンダリングされたときにアラートが出るようにしています。

     useEffect(() => {
            alert("コンポーネントがレンダリングされました!");
        });

    ここでは第二引数の[]を省略することで、レンダリングされる度に動くようにしています

    レンダリングされる度には動く処理であればuseEffectを使わずに直にalertを記述してもよさそうなものですが、実際これはエラーがでます。理由は後述します。

    「フォーカスを当てる」ボタンのクリックイベントのイベントハンドラーを記述しています。

      const handleFocus = () => {
            inputRef.current.focus();
            inputRef.current.value = "フォーカスされました!"; // ✅ `inputRef` を変更
        };

    ここではinputRefにフォーカスを当てる処理とvalueの値を「フォーカスされました!」に設定する処理を定義しています。

    続けて戻り値のJSXです。

    return (
            <div>
                <input ref={inputRef} type="text" placeholder="Type here..." />
                <button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>
                <button onClick={() => setCount(count + 1)}>カウント増加(useState変更)</button>
                <p>カウント: {count}</p>
            </div>
        );

    注目すべきなのは下記です。

    <input ref={inputRef} type="text" placeholder="Type here..." />

    このように、タグのrefプロパティにuseRefの値を設定することで、このタグをDOM操作できるようにしています。

    「フォーカスを当てる」ボタンタグのクリックイベントでこのinputタグをDOM操作できるようにしているわけです。

    <button onClick={handleFocus}>フォーカスを当てる(useRef変更)</button>

    このように、useRefはDOM操作をするときに使用します。

    副作用について

    今回のコードの中で、

     useEffect(() => {
            alert("コンポーネントがレンダリングされました!");
        });

    という、alertの処理をわざわざuseEffect内に実装する記述がありました。

    たとえば、

    alert("コンポーネントがレンダリングされました!");

    というように、コンポーネント内に直接記述するとエラーが発生します。

    これはなぜかというと、このalertの記述が「副作用」であるためです。

    副作用ってなんやねん?

    React のコンポーネントは基本的に 「入力 → 出力」 という流れで動くシンプルなもの。
    たとえば、次のようなコードは 「純粋な処理」 です。

    ✅ 純粋な処理(副作用なし)

    function add(a, b) {
        return a + b;
    }
    console.log(add(2, 3)); // 5
    

    この処理は「何かを変更したり影響を与えたりしない」ので、スッキリしてる!

    💩 副作用のある処理

    でも、もしこの関数の中で 「コンソールにログを出す」 とか 「画面のタイトルを変える」 みたいなことをすると…
    それは 「副作用(Side Effect)」 になります!

    function addWithEffect(a, b) {
        console.log("足し算が実行されました!"); // 💩 これは副作用
        document.title = `合計: ${a + b}`; // 💩 これも副作用
        return a + b;
    }
    console.log(addWithEffect(2, 3)); // 5

    🚨 「足し算をするだけのはずなのに、コンソール出力やタイトル変更をしてる!」
    🚨 「本来の目的(足し算)以外のことをしてる!」
    🚨 これが useEffect で管理すべき「副作用」なのだ!

    React における副作用

    React では、コンポーネントの役割は 「UI を描画すること」。
    でも、時々 「コンポーネントの外の世界」に影響を与えないといけないことがあります。(副作用)

    API リクエストサーバーからデータを取得する(fetch など)
    イベントリスナー
    window.addEventListener("scroll", ...) を設定
    タイマー処理setTimeoutsetInterval を使う

    こういった処理はuseEffectで管理することになっています。

    少しイメージしづらいですが、ルールとして覚えておきましょう。

  • Reace Hooks その⑤

    コンポーネントのアンマウントとは?

    コンポーネントのアンマウントとは「コンポーネントが画面から削除されること」です。

    コンポーネントが再レンダリング(マウント)されるということはその都度削除され、更新されているということです。

    クリーンアップ処理

    クリーンアップ処理(cleanup)とは、コンポーネントが削除(アンマウント)されたときに実行される処理 のことです。

    どういったときに使うのか、実際にコードを書いて確認していきます。

    app/components/scrollTracker.js

    "use client";
    
    import { useState, useEffect } from "react";
    
    function ScrollTracker() {
        const [scrollY, setScrollY] = useState(0);
    
        useEffect(() => {
            const handleScroll = () => {
                setScrollY(window.scrollY);
            };
    
            window.addEventListener("scroll", handleScroll);
            console.log("🎯 スクロールイベントリスナーを登録しました!");
    
            // ✅ クリーンアップ処理
            return () => {
                window.removeEventListener("scroll", handleScroll);
                console.log("🧹 スクロールイベントリスナーを削除しました!");
            };
        }, []); // ✅ 初回マウント時のみ実行
    
        return (
            <div style={{ height: "200vh", padding: "20px" }}>
                <p>スクロール位置: {scrollY}px</p>
            </div>
        );
    }
    export default ScrollTracker;

    page.js

    import ScrollTracker from "./components/scrollTracker";
    <ScrollTracker />

    下図のようにスクロール位置を表示するコンポーネントです。

    特筆すべきは下記コードです。

    window.addEventListener("scroll", handleScroll);
    window.removeEventListener("scroll", handleScroll);

    スクロールイベントを発火させるイベントリスナーとその削除です。

    こういったイベントリスナーを記述する際はuseEffect内に処理を記述し、且つクリーンアップ処理でイベントリスナーを削除するのがReactの作法となります。

    このようにクリーンアップ処理を記述しないと、

    • コンポーネントが削除されても、イベントリスナーが残り続ける
    • メモリリークが発生し、不要な setState が実行される可能性がある

    という事象が置きます。イベントリスナーを記述するときはuseEffect内でクリーンアップ処理をする。

    という風に覚えておきましょう。

  • Reace Hooks その④

    useEffectとは

    この記事からuseEffectについて見ていきます。

    useEffect は、コンポーネントが画面に表示されたり(マウント)、更新されたり、消えたり(アンマウント)するタイミングで実行される処理 を定義するための React のフック(Hook)です。

    基本的な使い方は下図です。

    マウントとは?

    まず、「マウント」という耳慣れない言葉について説明します。

    マウントとは、コンポーネントが最初に画面に表示されること を指します。
    つまり、コンポーネントが初めて DOM に追加されるタイミング です。

    useEffectの使い方

    説明だけだとわかりにくいと思いますので、実際にコードを書いて動かしてみましょう。

    「use-effect-app」アプリを作ります。

    npx create-next-app@latest use-effect-app

    appフォルダにcomponentsフォルダを作成しその中に「counter.js」を作ります。

    counter.js

    "use client";
    import { useState, useEffect } from "react";
    
    function Counter() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
            alert("コンポーネントがマウントされました。");
        }, []); // 依存配列が空 → 初回マウント時のみ実行
    
        return (
            <div>
                <p>カウント: {count}</p>
                <button onClick={() => setCount(count + 1)}>+1</button>
            </div>
        );
    }
    export default Counter;

    次にapp/page.jsでCounterコンポーネントを読み込みます。

    page.js

    import Counter from "./components/counter";
    
    export default function Home() {
      return (
        <Counter />
      );
    }

    開発サーバーを立てて確認してみましょう。

    npm run dev

    このようにアラートが出てくるかと思います。

    「+1」の部分をクリックしてみると、

    アラートは表示されません。

    このように、useEfectの第二引数の[]の中身を空にすると、初回マウント時のみ処理が動きます。

    別の例を見てみましょう。

    app/components/everyCounter.js

    "use client";
    import { useState, useEffect } from "react";
    
    function EveryCounter() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
            alert("カウンタアップしました。");
        }, [count]);  // ✅ `count` が変わるたびに実行される!
    
        return (
            <div>
                <p>カウント: {count}</p>
                <button onClick={() => setCount(count + 1)}>+1</button>
            </div>
        );
    }
    export default EveryCounter;

    page.js

    import EveryCounter from "./components/everyCounter";
     <EveryCounter />

    EveryCounter部分の「+1」をクリックする度にアラートが出ます。

    このようにuseEffectの第二引数の[]に値を設定すると、初回マウント時+その値が更新される度に処理が動きます。

    useEffectの機能

    useStateによってstateが更新されるとコンポーネントが再レンダリング(マウント)されます。

    これに対し、useEffect内の処理はコンポーネントの初回マウント時や設定した値の更新に応じて処理するタイミングを決められます。

    このようにレンダリング毎に毎回処理が動かないことが1つのメリットです。

  • Reace Hooks その③

    useStateで配列を更新しリアルタイムで表示を変える

    今回はuseStateで配列の値を管理し、それをリアルタイムで表示していきます。

    今回の書き方は管理者側の更新画面などでReactで頻繁に出てくるものなので、少し複雑ですが頑張っていきましょう。

    app/componentsフォルダに「todoList.js」を作成しTodoListコンポーネントを定義します。

    "use client";
    
    import { useState } from "react";
    
    export default function TodoList() {
        // 配列を `useState` で管理
        const [items, setItems] = useState([]);
        const [text, setText] = useState("");
    
        // 入力値を更新
        const handleChange = (e) => {
            setText(e.target.value);
        };
    
        // 配列に新しいアイテムを追加
        const addItem = () => {
            setItems([...items, text]); // 新しい配列を作成(スプレッド構文)
            setText(""); // 入力欄をクリア
        };
    
        return (
            <div>
                <h2>Todo List</h2>
                <input type="text" value={text} onChange={handleChange} />
                <button onClick={addItem}>追加</button>
    
                <ul>
                    {items.map((item, index) => (
                        <li key={index}>{item}</li>
                    ))}
                </ul>
            </div>
        );
    }
    

    次にapp/page.jsでTodoListコンポーネントを読み込みます。

    import TodoList from "./components/todoList";
    <TodoList />

    このように、inputタグに入力し、「追加」ボタンをクリックするとリストがリアルタイムで増えていきます。

    コード解説

    上からTodoListコンポーネント内のコードを順に解説していきます。

    useStateを使って、リスト用の配列「items」と入力値の管理用の値「text」を定義しています。

    初期値はそれぞれ空の配列と空の文字列です。

    const [items, setItems] = useState([]);
    const [text, setText] = useState("");

    次に、textを更新するイベントハンドラ「handleChange」関数を定義しています。

     const handleChange = (e) => {
            setText(e.target.value);
        };
    

    そしてitemsを更新するイベントハンドラ「addItem」関数を定義しています。

    const addItem = () => {
            setItems([...items, text]); // 新しい配列を作成(スプレッド構文)
            setText(""); // 入力欄をクリア
        };

    ここで見慣れない書き方がされています。

    setItems([...items, text]);

    これは、元のitemsをスプレッド構文で展開しそれにtextの値を足した新しい配列を新しいitems配列として定義・更新しています。

    stateで扱う値は直接変更できない。というルールがあるため、元の配列に要素を追加するのではなく、新たな配列として定義する形をとっています。

    また、下記のように値を使用したら空にしてクリアするのもReactでは一般的な作法となります。

     setText("");

    JSXでは「追加」ボタンをクリックしたらinputタグに入力されている値が<li>タグとして追加される記述をしています。

     return (
            <div>
                <h2>Todo List</h2>
                <input type="text" value={text} onChange={handleChange} />
                <button onClick={addItem}>追加</button>
    
                <ul>
                    {items.map((item, index) => (
                        <li key={index}>{item}</li>
                    ))}
                </ul>
            </div>
        );

    まず見慣れないのは<ul>の中身です。

      {items.map((item, index) => (
         <li key={index}>{item}</li>
      ))}

    mapメソッドを使ったリスト作成

    mapメソッドは元の配列の各要素を加工して新しい配列を作るJavascriptのメソッドです。

    今回の記述だと、items配列の各要素を基に<li>JSXタグを要素とする配列を作っています。

    最後に、indexとkeyについて説明します。

    <li key={index}>{item}</li>

    ここで「key」をmapメソッドの生成するindexの値を入れていますが、

    Reactではリストには「key属性」を必ず指定するというルールがあるためです。

    なぜこのようなルールがあるかについての詳細な説明は省きますが、ルールとして覚えておいてください。

    また、今回のindexの指定の仕方だと、実はリストの「変更」「削除」があるような場合には不適切です。

    こちらもまた今後解説します。

    textのstateを作った理由(※飛ばしてもOK)

    こちらは興味のある方のみでOKですが、今回stateで管理する値を

    • test
    • items

    の2つ用意しました。

    JQueryでのDOM操作に慣れている方なら、

    「addItem」関数内で直接inputタグの値を参照すればよいのでは?と思うかもしれません
    例:)

    const addItem = () => {
        const inputValue = document.querySelector("input").value; 
        setItems([...items, inputValue]);
    };

    これは実は動くことは動くのですが、以下の理由で推奨されません。

    1. 更新タイミングがずれる可能性がある(値が最新でない)。
    2. 入力欄のクリアが難しい。
    3. 直接 DOM を操作している(React の流儀に反する)。

    stateが更新される度に画面が更新(再レンダリング)されるため、stateを使わないと値が最新でない可能性があるというのが一番の理由になります。

    このstateが更新される度に画面が更新(再レンダリング)されるという特性は重要なReactの機能なので覚えておいてください。

  • Reace Hooks その②

    useStateでテキスト入力

    useStateを使って入力したテキストがリアルタイムで表示されるようにしてみましょう。

    まずはapp/componentsフォルダにtextInput.jsを作成しTextInputコンポーネントを定義します。

    "use client";
    
    import { useState } from "react";
    
    export default function TextInput() {
        const [text, setText] = useState("");
    
        return (
            <div>
                <input
                    type="text"
                    value={text}
                    onChange={(e) => setText(e.target.value)}
                />
                <p>You typed: {text}</p>
            </div>
        );
    }

    次にapp/page.jsでTextInputコンポーネントを呼び出して表示します。

    import TextInput from "./components/textInput";
    <TextInput />

    入力フォームに入力したテキストがリアルタイムで「You tiped:」の箇所に反映されます。

    コード解説

    textInput.jsのコードを解説していきます。

    クライアントコンポーネントの宣言、useStateのインポートをします。

    "use client";
    
    import { useState } from "react";

    textを更新するための設定をします。初期値は空テキスト「””」です。

     const [text, setText] = useState("");

    inputタグのonChangeイベントでtextの更新処理ができるように記述しています。

      return (
            <div>
                <input
                    type="text"
                    value={text}
                    onChange={(e) => setText(e.target.value)}
                />
                <p>You typed: {text}</p>
            </div>
        );

    見慣れない記述かと思いますが、ここでのイベントハンドラはアロー関数により定義されているので、下記のように定義したのと同じ意味になります。

     function handleChange(e) {
            setText(e.target.value);
        }
    
        return (
            <div>
                <input 
                    type="text"
                    value={text}
                    onChange={handleChange} 
                />
                <p>You typed: {text}</p>
            </div>

    アロー関数は「式」なのでJSX内に値としてそのまま埋め込めるというわけです。

    useStateでクラスのON・OFF

    次に、CSSとJavascriptを組み合わせて見た目を変化させる時によく使うクラスのON・OFFのやり方を見ていきましょう。

    まずはapp/componentsフォルダに「toggleButton.js」ファイルを作成し、下記コードを記述します。

    "use client";
    
    import { useState } from "react";
    import "./styles.css"; // CSSファイルを適用
    
    export default function ToggleButton() {
        // 初期状態は OFF(false)
        const [isActive, setIsActive] = useState(false);
    
        // アロー関数で ON / OFF を切り替える
        const toggleActive = () => setIsActive(prev => !prev);
    
        return (
            <button
                className={isActive ? "active" : ""}
                onClick={toggleActive}
            >
                {isActive ? "Active" : "Inactive"}
            </button>
        );
    }

    次にapp/componentsフォルダに「sytle.css」を作成して下記コードを記述します。

    button {
        padding: 10px 20px;
        font-size: 16px;
        border: none;
        cursor: pointer;
        background-color: lightgray;
        transition: 0.3s;
    }
    
    button.active {
        background-color: dodgerblue;
        color: white;
    }

    最後にapp/page.jsでToggleButtonコンポーネントを呼び出します。

    下記のようにInactiveボタンをクリックするとスタイルがあたります。

    コード解説

    toggleButton.jsのコードを解説していきます。

    • クライアントコンポーネントの宣言
    • useStateのインポート
    • style.cssの読み込み
    "use client";
    
    import { useState } from "react";
    import "./styles.css";

    isActiveを更新する設定をします。初期値は「false」です。

    const [isActive, setIsActive] = useState(false);

    イベントハンドラーtoggleActiveを定義します。

    const toggleActive = () => setIsActive(prev => !prev);

    見慣れない記述がありますが、これは関数型更新と呼ばれる書き方です。
    説明が長くなるので説明は後述します。

    JSX内ではbuttonタグがクリックされる度にisActiveがtureとfalseで反転する記述を書いています。

    return (
            <button
                className={isActive ? "active" : ""}
                onClick={toggleActive}
            >
                {isActive ? "Active" : "Inactive"}
            </button>
        );

    クラスはReactでは「class」ではなく「className」と書きます。

    {isActive ? "active" : ""}
    {isActive ? "Active" : "Inactive"}

    この記述は2つとも「三項演算子」です。

    isActiveの値が「true」か「false」かによって返す値を分けています。

    三項演算子は「式」なのでJSX内に値として{}の中に入れられます。

    関数型更新

    関数型更新とは?

    初期値ではisActiveは「false」で、「!」は値を反転させる記号なので、!isActiveは「true」となります。
    一度クリックするとisActiveは「true」に更新されて、!isActiveは「false」ということになります。
    このように記述することでクリックする度にtrueとfalseが反転する処理を記述しています。

    このように常に最新のstateの値を基にした処理を書きたいときにこの関数型更新を使用します。

    「常に最新のstateの値」とはどういうことか?

    例えば下記のようなコードを書いたとします。

    function Counter() {
        const [count, setCount] = useState(0);
    
        function handleClick() {
            setCount(count + 1);
            setCount(count + 1);
            setCount(count + 1);
        }
    
        return (
            <div>
                <p>Count: {count}</p>
                <button onClick={handleClick}>+3</button>
            </div>
        );
    }

    このコードだとbuttonタグをクリックする度に「3ずつ」数字が増えそうなものですが、実際は「1ずつ」しか増えません。
    これはuseStateが非同期で動くからなのですが、なぜそうなるのかの詳細な説明は省きます。

    上記の奥に「3ずつ」増やしたい場合は下記のように記述します。

    function Counter() {
        const [count, setCount] = useState(0);
    
        function handleClick() {
            setCount(prev => prev + 1);
            setCount(prev => prev + 1);
            setCount(prev => prev + 1);
        }
    
        return (
            <div>
                <p>Count: {count}</p>
                <button onClick={handleClick}>+3</button>
            </div>
        );
    }

    このように記述することでhandleClick関数の処理の中でcountの値が関数型更新により順番に1ずつ加算され、最終的に一回のクリックで「3ずつ」増えます。

    次回はuseStateで配列を更新する手順を見ていきましょう。

  • Reace Hooks その①

    Hooksとは

    React の Hooks(フック) とは、関数コンポーネントで「状態(state)」や「ライフサイクル」の機能を使うための仕組み です。頻繁に使うHooksの解説をしていきます。

    useState

    Hooksの一つであるuseStateについて見ていきます。

    useStateとは

    useStateとはコンポーネントが持つ「動的なデータ」を管理するために使うHooksです。

    Next.jsの構築

    まず、Next.jsを構築しましょう。構築するフォルダをVScodeで開きターミナルで下記コードを入力します。

    npx create-next-app@latest hooks-app

    このように表示されていればNext.js構築完了です。

    ページ作成

    次にapp/pages.jsのHomeコンポーネントの中のreturn内の記述を削除します。

    次に、appフォルダにcomponentsフォルダを作成し「useState.js」ファイルを作成、下記コードを記述してください。

    "use client";
    
    import { useState } from "react";
    
    export default function Counter() {
        const [count, setCount] = useState(0);
    
        // アロー関数でイベントハンドラを定義
        const handleClick = () => setCount(count + 1);
    
        return (
            <div>
                <p>カウント: {count}</p>
                <button onClick={handleClick}>+1</button>
            </div>
        );
    }

    最後にapp/page.jsでCounterコンポーネントを呼び出します。

    import Counter from "./components/useState";
    
    export default function Home() {
      return (
        <Counter />
      );
    }

    ターミナルで開発サーバーを起動しhttp://localhost:3000/にアクセスすると下のように表示されます。

    npm run dev

    「+1」をクリックするとカウントの横の数字が増えていきます。

    コード解説

    app/components/useState.jsのコードを上から解説していきます。

    これはuseStateを使うための準備で、このコンポーネントをクライアントコンポーネントとして扱うという宣言です。

    use client"; 

    詳細の説明は省きますが、Next.jsでは

    • サーバーコンポーネント
    • クライアントコンポーネント

    という区別があり、useStateはクライアントコンポーネントでしか使えないというルールがあります。

    useStateを使用するためにreactからインポートしています。

    import { useState } from "react";

    ここでuseStateを使ってcountの状態管理をするための設定をしています。

    const [count, setCount] = useState(0);

    これは下図のような仕組みに基づきます。

    ここでクリックイベント用の関数を定義しています。※イベントハンドラといいます。

    const handleClick = () => setCount(count + 1);

    この書き方はアロー関数といいます。下図のように書き換え可能です。

    アロー関数もよく使うので書き方に慣れておいてください。

    最後にbuttonタグをクリックするとhandleClick関数が発火し、setCount関数がcountの値を更新するというJSXの記述になります。

       return (
            <div>
                <p>カウント: {count}</p>
                <button onClick={handleClick}>+1</button>
            </div>
        );

    useStateの特徴

    • React の Hooks の 1 つ
    • 状態が更新と自動的に再レンダリング(再描画)される
    • state は直接変更できない更新用関数のsetStateで変更・更新する

    次回はuseStateの色々な使い方を見ていきましょう。

  • Next.js構築 その⑩

    スプレッド構文とは

    スプレッド構文について説明します。

    スプレッド構文は配列やオブジェクトを「展開」する機能を持ちます。

    まずは下記のようなpersonというオブジェクトがあったとします。

    const person = {
        name: "Alice",
        age: 25,
        city: "Tokyo"
    };
    

    このpersonの中身を複製したnewPersonというオブジェクトを作成したいとして、

    const newPerson = person;

    このように記述すると、Javascriptの特性上「複製」ではなくnewPersonが元のpersonのデータを指すという意味になります。

    これだと、newPersonの中身だけ変更したい場合など、複製した上でそのデータを扱う場合に不便です。そこで使用するのがスプレッド構文です。

    書き方は下記のように、複製したいpersonの前に「…」をつけます。

    const newPerson = { ...person };

    このように記述することで、personの中身を展開した上で複製し、新たなnewPersonを作成できます。

    今回はオブジェクトを例に出しましたが配列でも使用可能です。

    スプレッド構文を使ったデータの継承

    オブジェクトとスプレッド構文を使って複数のデータを継承してみましょう。

    まずはapp/components/offspringフォルダに「spreadGC.js」を作成し、SpreadGcコンポーネントを定義します。

    export default function SpreadGc(props) {
        return (
            <div>
                <h3>I am the grandchild.</h3>
                <p>Name: {props.name}</p>
                <p>Age: {props.age}</p>
                <p>Favorite Color: {props.color}</p>
            </div>
        );
    }

    次にapp/componentsフォルダに「spreadChild.js」を作成しSpreadChildコンポーネントを定義します。

    import SpreadGc from "./offspring/spreadGC";
    
    export default function SpreadChild(props) {
        return (
            <div>
                <h2>I am the child.</h2>
                {/* スプレッド構文で全てのpropsを孫に渡す */}
                <SpreadGc {...props} />
            </div>
        );
    }

    最後にapp/page.jsでSpreadChildコンポーネントを定義し、personオブジェクトをpropsとして渡します。

    import SpreadChild from "./components/spreadChild";
      const person = {
        name: "Alice",
        age: 25,
        color: "blue",
      };
     <h1>I am the parent.</h1>
     {/* スプレッド構文で複数プロパティを子に渡す */}
     <SpreadChild {...person} />

    このように表示されました

    propsはオブジェクト

    今回行ったスプレッド構文を使ったデータの継承を図にまとめると下記のようになります。

    この図でも記載のある通り、propsはオブジェクトです。

    その性質を利用したデータ継承方法ということです。

    propsの特徴

    propsはオブジェクトですが、通常のオブジェクトとは異なる特徴があります。

    • オブジェクトなので分割代入やスプレッド構文が使える
    • 読み取り専用(変更不可)
    • 何も渡されないと {} になる

    データ継承の他の方法

    ここまでpropsを使ったデータ継承を見てきました。

    しかし、

    親から子、子から孫に明示的に渡す必要があるので、深いネストでの管理が複雑になる場合がある

    というデメリットがあります。

    ですので、他の方法としてContext APIReduxのような状態管理ツールといったものがReactでは用意されています。

    今は「そういうものもあるんだな」程度に考えておいてください。