logo

【初心者向け】7つの主要React Hooks をコード付きで完全解説!

【初心者向け】7つの主要REACT HOOKS をコード付きで完全解説!...
プログラミング
目次
  • ファイル構成

  • 1. useState

  • 2. useEffect

  • 3. useContext

  • 4. useMemo

  • 5. useCallback

  • 6. useReducer

  • 7. useRef

  • 終わりに

  • 初心者向けにコード付きでReactを学ぶなら以下の7つの必須のHookを紹介します。

    1. useState
    2. useEffect
    3. useContext
    4. useMemo
    5. useCallback
    6. useReducer
    7. useRef
    7-react-hooks 
7つの主要React Hooks をコード付きで完全解説!

    デモサイトURL

    Git Labレポジトリ

    ファイル構成

    ファイル構成は自由にして頂いて結構です。

    ここでは, App.jsx に各コンポーネントをまとめて表示しています。

    App.jsx
    import Car from "./00_useState/Car";
    import Count from "./01_useEffect/Count"; //UseEffect Demo
    import Timer from "./01_useEffect/Timer";
    import GlobalState from "./02_useContext/GlobalState";
    import Memo from "./03_useMemo/Memo";
    import Todos from "./05_useReducer/Todos";
    import Facts from "./04_useCallback/Facts";
    import PersistValues from './06_useRef/PersistValues'
    import AccessDomElements from './06_useRef/AccessDomElements'
    
    function App() {
      return (
        <div style={{ maxWidth: "800px", margin: "0 auto", overflowX: "hidden", padding: "1rem" }}>
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useState</h1>
          <Car />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useEffect</h1>
          <Count />
          <Timer />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useContext</h1>
          <GlobalState />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useMemo</h1>
          <Memo />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useCallBack</h1>
          <Facts />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useReducer</h1>
          <Todos />
          <hr />
    
          <h1 style={{ fontSize: "2rem", textAlign: "center" }}>useRef</h1>
          <PersistValues />
          <AccessDomElements />
          <br />
          <br />
          <br />
          <br />
        </div>
      );
    }
    
    export default App;
    

    1. useState

    これは最も基本的なHookで、関数コンポーネント内でstate(状態)を持たせることができます。useStateを使うことで、コンポーネント内で保持するデータの値を設定し、その値を更新する関数を提供します。

    Codeを開く

    Car.jsx

    // object を使った状態の更新
    // 状態を更新すると、その状態全体が上書きされます。しかし、もし車の色だけを更新したい場合はどうでしょうか?
    // もしsetCar({color: "blue"})だけを呼び出したら、状態から車のブランド、モデル、年式が削除されてしまいます。
    // JavaScriptのスプレッド演算子を使うと、この問題を解決できます。
    import { useState } from "react";
    
    function Car() {
    	const [car, setCar] = useState({
    		brand: "Toyota",
    		model: "Hilux",
    		year: "2022",
    		color: "White"
    	});
    
    	const updateColor = () => {
    		// スプレッド演算子を使用して、現在の状態を関数に渡し、
    		// それを新しいオブジェクトに展開してから、変更または追加したい単一の値を指定できます。
    		setCar(currentState => {
    			return { ...currentState, color: "Black" } // shallow copy and adding color property
    		});
    	}
    
    	return (
    		<div>
    			<h2>My {car.brand}</h2>
    			<div>
    				It is a <span style={{ fontSize: "2rem" }}>{car.color} </span>{car.model} from {car.year}.
    			</div>
    			<button
    				type="button"
    				onClick={updateColor}
    				style={{ margin: "1rem 0" }}
    			>Black</button>
    		</div>
    	)
    }
    export default Car
    

    2. useEffect

    サイドエフェクト(副作用)を関数コンポーネント内で扱うためのHookです。データの取得、購読、手動でのDOMの変更など、レンダリングの結果として実行される処理を扱う際に用います。また、コンポーネントがマウントされた後やアップデートされた後に実行される処理を設定することができます。

    Codeを開く

    Count.jsx

    //? React useEffect Hook
    /*
    useEffectフックは、コンポーネント内で副作用を実行するために使います。
    データの取得、DOMの直接更新、タイマーの設定などが副作用の例です。
    useEffectは2つの引数を取りますが、2番目の引数はオプショナルです。
    */
    //* useEffect(<function>, <dependency>)
    
    //! Examples
    /*
    useEffect(() => {
    	Runs on every render
    });
    
    useEffect(() => {
    	Runs only on the first render
    }, []);
    
    useEffect(() => {
    	Runs on the first render
    	And any time any dependency value changes
    }, [prop, state]);
    */
    
    import { useState, useEffect } from "react";
    
    function Counter() {
    	const [count, setCount] = useState(0);
    	const [calculation, setCalculation] = useState(0);
    
    	// countの値が変わるたびにuseEffectが実行される
    	useEffect(() => {
    		setCalculation(() => count * 2); // 値に*2をして計算
    	}, [count]); // count の値を監視
    
    	return (
    		<div>
    			{/* Renders count */}
    			<p>Count: {count}</p>
    			{/* OnClick で	1を加算 */}
    			<button onClick={() => setCount((c) => c + 1)}>+</button>
    			{/* Render calculation */}
    			<p>Calculation: {calculation}</p>
    		</div>
    	);
    }
    export default Counter;
    Codeを開く

    Timer.jsx

    //? React useEffect Hook
    /*
    Some effects require cleanup to reduce memory leaks.
    Timeouts, event listeners, and others that are no longer needed should be disposed.
    We do this by including a return function at the end of the useEffect Hook.
    */
    //! Examples
    
    import { useState, useEffect } from "react";
    
    function Timer() {
    	const [count, setCount] = useState(0);
    
    	useEffect(() => {
    		// 1秒ごとにcountをインクリメントするタイマー
    		let timer = setTimeout(() => {
    			setCount((count) => count + 1);
    		}, 1000);
    
    		//2. Stop the timer once the UseEffect runs once
    		// クリーンアップ関数で、タイマーをクリアする
    		return () => clearTimeout(timer)
    	}, [count]);
    
    	return (
    		<>
    			<h2 style={{ textAlign: "center" }}>無限レンダリング</h2>
    			<h3>レンダリングを {count} 回しました!</h3>
    		</>
    	)
    }
    export default Timer;

    3. useContext

    ReactのContextを利用するためのHookです。Contextは、コンポーネントツリーを通してデータを渡す(”伝播”させる)ための方法を提供します。useContextを使用することで、コンテキストオブジェクトから現在のコンテキスト値を読み取ることができます。

    Codeを開く

    GlobalState.jsx

    //? useContext
    // React Contextは、グローバルに状態を管理する方法です。
    // useStateフックと組み合わせて使用することで、useStateだけを使うよりも深くネストされたコンポーネント間で状態を簡単に共有できます。
    // これにより、親コンポーネントに状態を持たせ、深くネストされた子コンポーネントがプロップドリリングなしで簡単に状態にアクセスできるようになります。
    
    
    //  Create context に必要なものをインポート
    import { useState, createContext, useContext } from "react";
    
    // 初期化することで、Contextを作成する
    const UserContext = createContext();
    
    function GlobalState() {
    	const [user] = useState("Kazu");
    
    	return (
    		// Context Provider を使用して、状態Contextが必要なコンポーネントのツリーをラップします。
    		<UserContext.Provider value={user}>
    			<h2>{`Hello ${user}!`}</h2>
    			{/* この中にあるすべてのコンポーネントは、user Contextにアクセスできるようになります。 */}
    			<Component2 />
    		</UserContext.Provider>
    	);
    }
    
    function Component2() {
    	return (
    		<>
    			<h2>Component 2</h2>
    			<Component3 />
    		</>
    	);
    }
    
    function Component3() {
    	return (
    		<>
    			<h2>Component 3</h2>
    			<Component4 />
    		</>
    	);
    }
    
    function Component4() {
    	return (
    		<>
    			<h2>Component 4</h2>
    			<Component5 />
    		</>
    	);
    }
    
    function Component5() {
    	// 子コンポーネントでContextを使用するには、useContextフックを使用してアクセスする必要があります。
    	const user = useContext(UserContext);
    
    	return (
    		<>
    			<h2>Component 5</h2>
    			{/* ここで、user Contextにアクセスできます。 */}
    			<h2>{`Hello ${user} again!`}</h2>
    		</>
    	);
    }
    export default GlobalState;

    4. useMemo

    メモ化された値を返すHookです。計算コストの高い関数の戻り値をメモ化することで、同じ入力に対しては再計算せずに済むようになり、パフォーマンスを向上させることができます。

    Codeを開く

    Memo.jsx

    //? useMemo
    
    //! Note
    /*
    ReactのuseMemoフックは、メモ化された値を返します。メモ化とは、値をキャッシュして再計算の必要をなくすことを指します。
    
    useMemoフックは、依存する値のいずれかが更新されたときにのみ実行されます。これにより、パフォーマンスが向上することがあります。
    
    useMemoとuseCallbackフックは似ていますが、主な違いはuseMemoがメモ化された値を返すのに対し、useCallbackはメモ化された関数を返す点です。
    */
    
    import { useState, useMemo } from "react"; //1. Import useMemo
    
    const Memo = () => {
      const [count, setCount] = useState(0);
    
      // `useMemo`は、実行したいコストの高い関数と監視したい値を引数に取ります。監視している値が更新された場合にのみ、そのコストの高い関数を実行します。
      // これにより、監視している値(例えば count)が変化したときにのみ関数を実行することで、アプリのパフォーマンスを最適化します。
    
      // count の値が変わるたびに, レンダリングコストの高い関数が実行される
      const calculation = useMemo(() => expensiveCalculation(count), [count]);
    
      //4. Function to update the value we are watching (count)
      const increment = () => {
        setCount((c) => c + 1); // 現在の値に+1を設定
      };
    
      return (
        <div style={{ width: "800px", margin: "0 auto" }}>
          <div>
            Count: {count}
            <button onClick={increment}>+</button>
            <h2>Expensive Calculation</h2>
            {calculation}
          </div>
        </div>
      );
    };
    
    //5. This is a pointless expensive function that loop 1000000 times
    const expensiveCalculation = (num) => {
      console.log("Calculating...");
      for (let i = 0; i < 1000000; i++) {
        num += 1;
      }
      return num;
    };
    
    export default Memo;
    

    5. useCallback

    メモ化されたコールバック関数を返すHookです。特に、子コンポーネントにpropsとして渡す関数を効率的に再利用するために使用されます。これにより、不必要なレンダリングを防ぐことができます。

    Codeを開く

    Card.jsx

    import { useEffect, useState } from "react";
    
    function CarduseCallBack(props) {
      const { getFacts } = props;
      const [catFacts, setCatFacts] = useState(); // Stores the cat facts from the API
    
      // useEffect will only run if the getFacts function is updated.
      useEffect(() => {
        // Create async function to call getFacts function
        async function callAPI() {
          const facts = await getFacts()
          // console.log(facts);
          setCatFacts(facts);
        }
        callAPI()
      }, [getFacts]);
    
      // Render facts
      return (
        <div>
          {Array.isArray(catFacts)
            ? catFacts.map((fact, index) => {
              // console.log(fact);
              return <div
                key={index}
                style={{ border: "1px solid black", margin: "10px", padding: "10px" }}
              >
                {index + " - " + fact.fact}
              </div>
            })
            : console.log("Not loaded")}
        </div >
      );
    }
    
    export default CarduseCallBack;
    Codeを開く

    Facts.jsx

    //? useCallback
    // Reactの`useCallback`フックは、メモ化されたコールバック関数を返します。メモ化とは、値をキャッシュして再計算の必要をなくすことです。
    // `useCallback`を使用する理由は、リソースを大量に消費する関数を隔離し、毎回のレンダー時に自動的に実行されるのを防ぐためです。
    // このフックは、依存関係のいずれかが更新されたときにのみ実行され、パフォーマンスを向上させることができます。
    
    //!Note
    /*
    親コンポーネントで関数を定義し、子コンポーネントに渡す場合、子コンポーネントが再レンダリングされるたびに、関数が再定義されます。
    なので、`useCallback`を使用することで、関数が再定義されるのを防ぐことができます。
    */
    import React, { useState, useCallback } from "react"; //  import useCallback
    import axios from "axios";
    
    // card component
    import Card from "./Card";
    
    function Facts() {
      const [limit, setLimit] = useState(3); //2. Sets limit for number of facts we want back
    
      //useCallbackは関数を保存し、その関数はlimit値が変更された場合にのみ再作成されます。
      // これにより、毎回のレンダーで関数が再作成されることなくコードを最適化できます。
      // 特に、関数をpropとして渡す場合に便利で、必要がなければコンポーネントが再レンダーされなくなります。
      const getFacts = useCallback(async () => {
        let response = await axios.get(
          `https://catfact.ninja/facts?limit=${limit}`
        );
        return response.data.data;
      }, [limit]);
    
      const handleIncrementFacts = () => {
        console.log("test");
        setLimit((l) => l + 1);
      };
    
      return (
        <div className="App">
          <button onClick={() => handleIncrementFacts()}>+1 Fact</button>
          <div className="row p-1">
            <div className="col">
              {/* 関数をコンポーネントに渡します。このコンポーネントは、`getFacts`関数が変更されたときにのみ再レンダリングされます。*/}
              <Card getFacts={getFacts}></Card>
            </div>
          </div>
        </div>
      );
    }
    
    export default Facts;
    
    

    6. useReducer

    Codeを開く

    Todos.jsx

    //?useReducer
    // useReducerフックはuseStateフックに似ていますが、カスタムの状態ロジックを可能にします。
    // useReducerは、状態に保存する前にデータを操作するコードを使用できるようにします。
    // 複雑なロジックに依存する複数の状態を追跡する必要がある場合、useReducerが役立つかもしれません。
    
    import { useReducer } from "react"; // useState の代わりに useReducer を使用
    
    // useReducerのためのデータ
    const initialTodos = [
      {
        id: 1,
        title: "Todo 1",
        complete: false, // ここをtrueにすると、最初からチェックが入った状態になる
      },
      {
        id: 2,
        title: "Todo 2",
        complete: false,
      },
    ];
    
    // The logic for the reducer
    // state = 現在のuseReducerの状態
    // action = reducer functionに渡すデータ
    const reducer = (state, action) => {
      switch (action.type) {
        //if the type is = COMPLETE use the id to find the correct object and update it to COMPLETE
        // typeがCOMPLETEの場合、idを使用して正しいオブジェクトを見つけ、COMPLETEを更新します。
        case "COMPLETE":
          return state.map((todo) => {
            if (todo.id === action.id) {
              return { ...todo, complete: !todo.complete }; // shallow copy してから、todoのcompleteを反転 (reactのimmutableの更新方法)
            } else {
              return todo;
            }
          });
        default:
          return state;
      }
    };
    
    function Todos() {
      // reducerのセットアップについて説明
      // initialTodosは初期データ、reducerはデータを変更するロジックを持つ関数です (上で定義済み)
      // dispatchは入力を受け取りreducerロジックを実行する関数を呼び出します。
      // そして、todosはreducerの結果として得られるデータです。
      const [todos, dispatch] = useReducer(reducer, initialTodos);
    
      // Reducer関数を呼び出して状態を更新することができます。
      const handleComplete = (todo) => {
        dispatch({ type: "COMPLETE", id: todo.id });
      };
    
      return (
        <>
          {todos.map((todo) => (
            <div key={todo.id}>
              <label>
                <input
                  type="checkbox"
                  checked={todo.complete} // true or false
                  onChange={() => handleComplete(todo)}
                />
                {todo.title}
              </label>
            </div>
          ))}
        </>
      );
    }
    export default Todos;
    

    7. useRef

    refオブジェクトを作成するためのHookです。useRefは、DOM要素への参照を保持するのによく使われますが、レンダリング間でのデータの保持にも使用できるという、変更可能なオブジェクトを提供します。

    Codeを開く

    AccessDomElements.jsx

    //? Access the DOM
    //通常、ReactにDOMを操作させるのが最も安全なアプローチですが、何らかの理由で直接DOMにアクセスする必要がある場合もあります
    // そのような時、useRefを使用して操作したいDOM要素への参照を保存することが可能
    //* Reactでは、要素にref属性を追加することで、DOM内のその要素に直接アクセスすることができます。
    
    import { useRef } from "react";
    
    function AccessDomElements() {
    	// 変数を作成し、useRefを使用します。これにより、DOM要素への参照を保存することができます
    	const inputElement = useRef();
    
    	const focusInput = () => {
    		// ボタンがクリックされた時に、inputElement内の現在の値にアクセスし、それにfocus()を設定
    		inputElement.current.focus();
    	};
    
    	return (
    		<>
    			{/* ref属性を使用して、DOM要素をinputElement変数に保存します*/}
    			<input type="text" ref={inputElement} />
    			{/* DOM要素を保存する変数を持っているので、それにアクセスできます */}
    			<button onClick={focusInput}>Focus Input</button>
    		</>
    	);
    }
    
    export default AccessDomElements;
    Codeを開く

    PersistValues.jsx

    //? useRef
    //useRefフックは、レンダリング間で値を保持することができます
    // これは、更新しても再レンダリングを引き起こさない変更可能な値を保存するために使用できます
    // 例えば、状態の値を変更すると再レンダリングが発生しますが、useRefを使用すれば、DOM要素に直接アクセスすることもできます
    
    import { useState, useEffect, useRef } from "react";
    
    function PersistValues() {
    	// この状態を更新して再レンダリングをトリガー
    	const [inputValue, setInputValue] = useState("");
    	// Countはレンダリングの回数を保存
    	const count = useRef(0); //initial value is set to 0
    
    	// useEffectは、再レンダリングのたびに実行される
    	useEffect(() => {
    		// カウントを1増やします
    		// useRefを使用することは、const count = {current: 0}とすることに似ています
    		// count.currentを使用してカウントにアクセスできる
    		count.current = count.current + 1;
    	});
    
    	return (
    		<>
    			{/* onChange は状態をアップデートするために使われる */}
    			<input
    				type="text"
    				value={inputValue}
    				onChange={(e) => setInputValue(e.target.value)}
    			/>
    			{/*  Render the useRef value */}
    			<h2>Render Count: {count.current}</h2>
    
    			<br />
    		</>
    	);
    }
    
    export default PersistValues

    終わりに

    この記事では、Reactの学習に7つの主要なHooksについて初心者向けにコード例と共に紹介しています。

    取り上げられているHooksは、useState(状態管理)、useEffect(副作用の管理)、useContext(グローバルな状態の共有)、useMemo(計算結果のメモ化)、useCallback(関数のメモ化)、useReducer(状態更新ロジックの外部化)、そしてuseRef(DOM要素への参照保持)です。

    これらはReactの関数コンポーネントでより効率的かつ効果的に状態管理や再レンダリングを制御するために使用されます。

    人気記事

    メルボルン移住(ワーホリ)を検討している方へ
    オーストリア
    メルボルン移住(ワーホリ)を検討している方へ
    メルボルンで絶対に行きたい場所10選
    オーストリア
    メルボルンで絶対に行きたい場所10選
    『ネイティブ監修』 オーストラリア厳選スラング30
    英語
    『ネイティブ監修』 オーストラリア厳選スラング30
    海外のWEBエンジニア特化スクールの費用や授業内容や費用など完全解説(オーストラリア・メルボルン)
    オーストリア
    海外のWEBエンジニア特化スクールの費用や授業内容や費用など完全解説(オーストラリア・メルボルン)
    • Kazu

      Kazu

      オーストラリア在住歴7年。現地で英語学習後に日本語教師 → 工場就職 → 海外WEB開発ディプロマを取得→フリーランス。現在は余った時間をプログラミング&ブログに費やして生きています。

    70