第3回の今回は複数のコンポーネントを作ったときの値の共有方法について解説します。
記事見出し一覧
-
第1回【React】:React入門 — コンポーネントとJSXの基本
- Reactとは何か、コンポーネントについて学ぶ
-
第2回【React】:フック入門 — useState と useEffect
- useState と useEffectというReactの関数を学ぶ
-
第3回【React】:コンポーネント間で値を共有する方法
- 複数のコンポーネントを作ったときの値の共有方法を学ぶ
-
第4回【Next.js】:Next.jsの基本
- Next.jsとは、ルーティングについて学ぶ
-
第5回【Next.js】:(補足)サーバーコンポーネントとクライアントコンポーネント
- サーバーコンポーネントとクライアントコンポーネントについて学ぶ
第3回:コンポーネント間で値を共有する方法
なぜ値共有が必要なのか
例えばToDoアプリを作成したときに下記のような問題があります。
ToDoアプリで下記のようなコンポーネントの構成になったとします。
App
├── TodoInput (タスク入力フォーム)
└── TodoList (タスク一覧)
TodoListで持っているタスク一覧情報に、別コンポーネントのTodoInputからはアクセスできません。そのため、フォームで入力したタスクをタスク一覧に追加することができないのです。
(自身のコンポーネントで定義したuseStateにしかアクセスできない(importもできない))
その対処方法として、propsという親から子コンポーネントへ値や関数を渡す方法があり、それを利用することで、TodoInputとTodoListの両方で同じuseState(タスク一覧)を参照・更新できるようになります。
やり方としては参照する対象のコンポーネントが共通で持つ親コンポーネントでuseStateを定義することです。
この方法を**stateのリフトアップ(lifting state up)**と呼びます。
App ←ここでタスク一覧情報を定義する
├── TodoInput (タスク入力フォーム) ←Appからpropsでタスク一覧情報へsetする関数を渡してもらう
└── TodoList (タスク一覧) ←Appからpropsでタスク一覧情報の値を渡してもらう
重要なポイント:
Reactのpropsによる値渡しは親から子への一方通行のみ。
- 子から親への直接的な値渡しは不可
- 兄弟関係(同じ階層)間での直接的な値渡しは不可
そのため必要な情報は共通の親で持ちます。
propsを使ったコード例
説明した内容を踏まえたアプリを作成してみました。
(propsで値や関数を渡す簡易的なTodoアプリを作成)
ToDoアプリ
:
テキストボックスに値を入力しボタンを押すと入力した値がリストに追加されるというもの。
ファイル構成
src/
├── App.jsx // メインコンポーネント
├── TodoInput.jsx // タスク入力フォームコンポーネント
├── TodoList.jsx // タスク一覧コンポーネント
└── App.css // デザイン用css
App.jsx
主な役割:
useStateを使ってToDo一覧の値の保持。ToDoに対して追加、削除を行う関数の定義。
import { useState } from "react";
import TodoInput from "./TodoInput.jsx";
import TodoList from "./TodoList.jsx";
import "./App.css";
function App() {
// ここで状態を管理
const [todos, setTodos] = useState([]);
// タスク追加の関数
const addTodo = (text) => {
const newTodo = {
id: Date.now(),
text: text,
completed: false,
};
setTodos([...todos, newTodo]);
};
// タスク削除の関数
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<div>
<h1>ToDoリスト</h1>
{/* 子コンポーネントに関数と値をpropsで渡す */}
<TodoInput onAdd={addTodo} />
<TodoList todos={todos} onDelete={deleteTodo} />
</div>
);
}
export default App;
コードについて:
TodoInput.jsxとTodoList.jsx でToDoリストの値を使用するため、それらの共通の親であるApp.jsxで[todos, setTodos]を定義している。
TodoInput.jsx
主な役割:
親App.jsxから渡されたToDo追加関数をpropsで受け取る。
import { useState } from "react"; // ← 追加!
function TodoInput({ onAdd }) {
const [inputValue, setInputValue] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue.trim()) {
// 親から受け取った関数を実行
onAdd(inputValue);
setInputValue(""); // 入力欄をクリア
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="新しいタスクを入力"
/>
<button type="submit">追加</button>
</form>
);
}
export default TodoInput;
コードについて:
フォームでの入力値を親コンポーネントからpropsで渡された関数onAdd(ToDo追加関数)を受け取り、フォームの値をonAddに対して渡すことでToDoに追加される。
TodoList.jsx
主な役割:
親App.jsxから渡されたToDo値とToDo削除関数をpropsで受け取る。
ToDo値をリストで表示し、削除関数を呼び出してToDoから削除を行う。
function TodoList({ todos, onDelete }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text}
{/* 親から受け取った削除関数を実行 */}
<button onClick={() => onDelete(todo.id)}>削除</button>
</li>
))}
</ul>
);
}
export default TodoList;
コードについて:
フォームでの入力値を親コンポーネントからpropsで渡されたonDelete関数(ToDo削除関数)を受け取り、削除ボタンを押した時にonDelete関数に削除対象のIDを引数にして実行し、ToDo一覧から該当のidを削除している。
また、todos変数でToDo一覧の値を受け取り<ul><li>を使ってリスト表示している。
コードに関する基本文法
分割代入(JavaScript)
function TodoList({ todos, onDelete }) {
この{ }を使った書き方は分割代入という構文です。
これは以下のように書くのと同じ意味です:
function TodoList(props) {
const todos = props.todos;
const onDelete = props.onDelete;
// ...
}
分割代入を使うことで、関数の引数の時点でprops.todosとprops.onDeleteを取り出して、todosとonDeleteという変数として使えるようになります。
コード内で毎回props.と書かずに済むためコードを短く書くことができます。
次回
次回はNext.jsについての内容に入り、複数ページを表示するルーティングについて学びます。