【初心者向け】7つの主要React Hooks をコード付きで完全解説!
初心者向けにコード付きでReactを学ぶなら以下の7つの必須のHookを紹介します。
- useState
- useEffect
- useContext
- useMemo
- useCallback
- useReducer
- useRef
ファイル構成
ファイル構成は自由にして頂いて結構です。
ここでは, 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の関数コンポーネントでより効率的かつ効果的に状態管理や再レンダリングを制御するために使用されます。
人気記事
Kazu
オーストラリア在住歴7年。現地で英語学習後に日本語教師 → 工場就職 → 海外WEB開発ディプロマを取得→フリーランス。現在は余った時間をプログラミング&ブログに費やして生きています。
70