概要: このチュートリアルでは、React コンテキストについて、そしてReact アプリ全体で状態を共有するためにどのように使用するかを学習します。
React コンテキスト入門
React コンテキストは、React の機能であり、コンポーネントツリーのすべてのレベルにpropsを渡すことなく、React アプリ全体(またはその一部)で状態を共有することを可能にします。
React コンテキストは、異なるネストレベルの多くのコンポーネントが状態の一部にアクセスする必要がある場合に役立ちます。
React コンテキストを実装するためのステップバイステップガイドを以下に示します。
ステップ 1. コンテキストを作成する
まず、react
ライブラリの createContext()
関数を使用してコンテキストを作成します。
// MyContext.js
import { createContext } fronm 'react';
const MyContext = createContext();
Code language: JavaScript (javascript)
ステップ 2. プロバイダーコンポーネントを作成する
プロバイダーコンポーネントは、コンテキストオブジェクトを使用して、共有状態を子コンポーネントに提供します。
// MyProvider.js
import { useState } from 'react';
import MyContext from './MyContext';
const MyProvider = ({ children }) => {
const [state, setState] = useState();
const shared = {state, setState };
return (
<MyContext.Provider value={shared}>
{children}
</MyContext.Provider>
);
};
export default MyProvider;
Code language: JavaScript (javascript)
この例では、Provider
コンポーネントは、state
と setState
関数を含むオブジェクト(shared
)を子コンポーネントに共有します。
state
と setState
関数に加えて、共有オブジェクトに配置することで任意の関数を共有できます。
ステップ 3. アプリでプロバイダーコンポーネントを使用する
プロバイダーコンポーネント内でコンポーネントツリーをラップして、すべての子コンポーネントがコンテキストオブジェクトにアクセスできるようにします。
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import MyProvider from './MyProvider';
const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);
root.render(
<MyProvider>
<App />
</MyProvider>
);
Code language: JavaScript (javascript)
この例では、App
コンポーネントとそのすべての子コンポーネントは、Provider
コンポーネントによって共有される MyContext
オブジェクトにアクセスできます。
ステップ 4. コンポーネントでコンテキストを使用する
Provider
コンポーネントの子コンポーネントから MyContext
オブジェクトを使用(またはアクセス)する方法を以下に示します。
import { useContext } from 'react';
import MyContext from './MyContext';
const MyComponent = () => {
const { state, setState } = useContext(MyContext);
return (
<div>
<p>{state.someValue}</p>
<button onClick={() => setState({ ...state, someValue: 'New Value' })}>
Update Value
</button>
</div>
);
};
export default MyComponent;
Code language: JavaScript (javascript)
コンテキストを使用して Todo アプリをリファクタリングする
Todo アプリをリファクタリングして、React コンテキストを使用してみましょう。
この図では
- App コンポーネントは、コンテキストから
getTodos
関数にアクセスして、すべての todo アイテムを取得します。 - TodoList コンポーネントは、
todos
状態にアクセスして、すべての todo アイテムを表示します。 - TodoShow コンポーネントは、
changeTodo
関数とremoveTodo
関数にアクセスします。 - TodoCreate コンポーネントは、
createTodo
関数にアクセスして、新しい todo アイテムを作成します。
1) React コンテキストの作成
まず、src
ディレクトリ内に context
という新しいディレクトリを作成します。
次に、コンテキストオブジェクトと Provider
コンポーネントを格納する todos.js
ファイルを作成します。
import { createContext, useState } from 'react';
const TodosContext = createContext();
const Provider = ({ children }) => {
const [todos, setTodos] = useState([]);
const getTodos = async () => {
const url = 'http://localhost:5000/todos';
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const storedTodos = await response.json();
// Update the state
if (storedTodos) setTodos(storedTodos);
} catch (error) {
console.error('Error during GET request:', error);
}
};
const createTodo = async (title) => {
// call an API to create a new todo
const url = 'http://localhost:5000/todos';
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: title,
completed: false,
}),
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const responseData = await response.json();
const newTodo = { ...responseData };
// add the new todo to the list
const updatedTodos = [...todos, newTodo];
setTodos(updatedTodos);
} catch (error) {
console.error('Error creating a todo:', error);
}
};
const removeTodo = async (id) => {
// Delete the todo
const url = `http://localhost:5000/todos/${id}`;
try {
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
// Update the state
const updatedTodos = todos.filter((todo) => todo.id !== id);
setTodos(updatedTodos);
} catch (error) {
console.error('Error during DELETE request:', error);
throw error;
}
};
const changeTodo = async (id, newTitle, completed = false) => {
// Update todo
const url = `http://localhost:5000/todos/${id}`;
const data = { title: newTitle, completed };
try {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const responseData = await response.json();
// Update the state
const updatedTodos = todos.map((todo) => {
if (todo.id === id) {
return { ...todo, ...responseData };
}
return todo;
});
setTodos(updatedTodos);
} catch (error) {
console.error('Error during PUT request:', error);
throw error;
}
};
const shared = { todos, getTodos, createTodo, removeTodo, changeTodo };
return (
<TodosContext.Provider value={shared}>{children}</TodosContext.Provider>
);
};
export default TodosContext;
export { Provider };
Code language: JavaScript (javascript)
仕組み。
まず、react ライブラリから createContext
関数と useState
関数をインポートします。
import { createContext, useState } from 'react';
Code language: JavaScript (javascript)
次に、createContext
関数を使用して、TodosContext
という新しい Context
オブジェクトを作成します。
const TodosContext = createContext();
Code language: JavaScript (javascript)
3 番目に、Provider コンポーネントを定義します。
const Provider = ({ children }) => {
// ...
}
Code language: JavaScript (javascript)
4 番目に、`useState` 関数を使用して Provider コンポーネントの状態を作成します。
const [todos, setTodos] = useState([]);
Code language: JavaScript (javascript)
5 番目に、getTodos
、createTodo
、removeTodo
、および changeTodo
関数を App コンポーネントから Provider コンポーネントに移動します。
6 番目に、コンポーネント間で共有される状態と関数を含むオブジェクトを定義します。
const shared = { todos, getTodos, createTodo, removeTodo, changeTodo };
Code language: JavaScript (javascript)
7 番目に、共有オブジェクトを Provider
コンポーネントの value
prop に渡して、Provider コンポーネントを返します。
return (
<TodosContext.Provider value={shared}>{children}</TodosContext.Provider>
);
Code language: JavaScript (javascript)
最後に、デフォルトエクスポートを使用して TodosContext
をエクスポートし、名前付きエクスポートを使用して Provider コンポーネントをエクスポートします。
export default TodosContext;
export { Provider };
Code language: JavaScript (javascript)
Provider コンポーネントを使用する
`index.js` ファイルで`App`コンポーネントを `Provider` コンポーネントでラップするように `index.js` を変更します。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from './context/todos';
const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);
root.render(
<Provider>
<App />
</Provider>
);
Code language: JavaScript (javascript)
仕組み。
まず、コンテキストから Provider
コンポーネントをインポートします。
import { Provider } from './context/todos';
Code language: JavaScript (javascript)
次に、App
コンポーネントを Provider
コンポーネントでラップします。
root.render(
<Provider>
<App />
</Provider>
);
Code language: JavaScript (javascript)
App
コンポーネントとすべての子コンポーネントは、Provider
コンポーネントによって提供される TodosContext
オブジェクトにアクセスできるようになります。
App コンポーネントの変更
`App` コンポーネントを以下のように変更します。
import TodoList from './components/TodoList';
import TodoCreate from './components/TodoCreate';
import { useEffect, useContext } from 'react';
import TodosContext from './context/todos';
import './App.css';
const App = () => {
const { getTodos } = useContext(TodosContext);
useEffect(() => {
getTodos();
}, []);
return (
<main className="main">
<h1>
React Todo <span>Streamline Your Day, the React Way!</span>
</h1>
<TodoList />
<TodoCreate />
</main>
);
};
export default App;
Code language: JavaScript (javascript)
仕組み。
まず、TodosContext
オブジェクトをインポートします。
import TodosContext from './context/todos';
Code language: JavaScript (javascript)
次に、useContext()
関数を使用して TodosContext
から getTodos
関数にアクセスします。
const { getTodos } = useContext(TodosContext);
Code language: JavaScript (javascript)
3 番目に、useEffect
フック内で getTodos
関数を呼び出します。
useEffect(() => {
getTodos();
}, []);
Code language: JavaScript (javascript)
TodoList コンポーネントの変更
`TodosContext` を使用する `TodoList` コンポーネントを変更します。
import TodoShow from './TodoShow';
import TodosContext from '../context/todos';
import { useContext } from 'react';
const TodoList = () => {
const { todos } = useContext(TodosContext);
const renderedTodos = todos.map((todo) => {
return <TodoShow key={todo.id} todo={todo} />;
});
return <ul className="todo-list">{renderedTodos}</ul>;
};
export default TodoList;
Code language: JavaScript (javascript)
仕組み。
まず、TodosContext
をインポートします。
import TodosContext from '../context/todos';
Code language: JavaScript (javascript)
次に、useContext
関数をインポートします。
import { useContext } from 'react';
Code language: JavaScript (javascript)
3 番目に、TodosContext
から todos 配列にアクセスします。
const { todos } = useContext(TodosContext);
Code language: JavaScript (javascript)
最後に、各 todo アイテムを `TodoShow` コンポーネントに渡します。
const renderedTodos = todos.map((todo) => {
return <TodoShow key={todo.id} todo={todo} />;
});
Code language: JavaScript (javascript)
TodoShow コンポーネントの変更
`TodosContext` を使用するように `TodoShow.js` コンポーネントを変更します。
import { useState } from 'react';
import TodoEdit from './TodoEdit';
import EditIcon from '../edit.svg';
import DeleteIcon from '../delete.svg';
import { useContext } from 'react';
import TodosContext from '../context/todos';
const TodoShow = ({ todo }) => {
const [showEdit, setShowEdit] = useState(false);
const { removeTodo, changeTodo } = useContext(TodosContext);
const handleDelete = (e) => {
removeTodo(todo.id);
};
const handleEdit = (e) => {
setShowEdit(!showEdit);
};
const handleSubmit = (id, title) => {
changeTodo(id, title);
setShowEdit(false);
};
const handleDoubleClick = (e) => {
e.preventDefault();
changeTodo(todo.id, todo.title, !todo.completed);
setShowEdit(false);
};
if (showEdit)
return (
<li className="todo">
<TodoEdit todo={todo} onSubmit={handleSubmit} />
</li>
);
else {
return (
<li className="todo" onDoubleClick={handleDoubleClick}>
<p className={todo.completed ? 'completed' : 'open'}>{todo.title}</p>
<div className="actions">
<button onClick={handleDelete}>
<img src={DeleteIcon} title="Delete" />
</button>
<button onClick={handleEdit}>
<img src={EditIcon} title="Edit" />
</button>
</div>
</li>
);
}
};
export default TodoShow;
Code language: JavaScript (javascript)
仕組み。
まず、react ライブラリから useContext
関数を、todos.js
モジュールから TodosContext
オブジェクトをインポートします。
import { useContext } from 'react';
import TodosContext from '../context/todos';
Code language: JavaScript (javascript)
次に、コンテキストから removeTodo
関数と changeTodo
関数にアクセスします。
const { removeTodo, changeTodo } = useContext(TodosContext);
Code language: JavaScript (javascript)
TodoCreate コンポーネントの変更
`TodosContext` オブジェクトを使用するように `TodoCreate` コンポーネントを変更します。
import { useState, useContext } from 'react';
import TodosContext from '../context/todos';
const TodoCreate = () => {
const [todo, setTodo] = useState('');
const { createTodo } = useContext(TodosContext);
const handleSubmit = (e) => {
e.preventDefault();
createTodo(todo);
setTodo('');
};
const handleChange = (e) => {
setTodo(e.target.value);
};
return (
<form onSubmit={handleSubmit} className="todo-create">
<input
type="text"
name="todo"
id="todo"
placeholder="Enter a todo"
value={todo}
onChange={handleChange}
/>
</form>
);
};
export default TodoCreate;
Code language: JavaScript (javascript)
仕組み。
まず、TodosContext
オブジェクトをインポートします。
import TodosContext from './context/todos';
Code language: JavaScript (javascript)
次に、useContext()
関数を使用して TodosContext
から createTodo
関数にアクセスします。
const { createTodo } = useContext(TodosContext);
Code language: JavaScript (javascript)
3 番目に、createTodo
関数を呼び出して、新しい todo アイテムを作成します。
const handleSubmit = (e) => {
e.preventDefault();
createTodo(todo);
setTodo('');
};
Code language: JavaScript (javascript)
プロジェクトのソースコードをダウンロードする
コンテキストを使用した React Todo アプリをダウンロードする
まとめ
- React アプリ全体で状態を共有するには、React コンテキストを使用します。