useReducerとuseStateの比較
useState
とuseReducer
は、Reactで状態管理を行うためのフックですが、それぞれ異なる用途と特性があります。
useState
-
用途: シンプルな状態管理。状態が1つまたは少数の場合に適しています。
-
構文:
const [state, setState] = useState(initialState);
-
状態更新:
- 状態を直接変更するための関数(
setState
)を使用します。 - 更新は即時の状態を反映しない場合があるため、前の状態に依存する場合は関数形式で更新する必要があります。
- 状態を直接変更するための関数(
-
例:
const [count, setCount] = useState(0); const increment = () => setCount(count + 1); // 状態を直接変更 // もしくは const increment = () => setCount(prevCount => prevCount + 1); // 前の状態に依存
useReducer
-
用途: 複雑な状態管理や、状態の変更が多い場合に適しています。特に、状態がオブジェクトであったり、複数の状態が関連している場合に有効です。
-
構文:
const [state, dispatch] = useReducer(reducer, initialState);
-
状態更新:
- 状態更新はアクションをディスパッチすることで行います。アクションにはタイプと必要なデータが含まれます。
-
reducer
関数が状態とアクションを受け取り、新しい状態を返します。
-
例:
const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } const [state, dispatch] = useReducer(reducer, initialState); const increment = () => dispatch({ type: 'increment' }); // アクションをディスパッチ
useReducerの実装パターン別の実装例
以下に、useReducer
を使った異なる実装パターンの雛形を示します。
1. 基本的なカウンター
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
2. 複数の状態を持つ
import React, { useReducer } from 'react';
const initialState = { count: 0, text: '' };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setText':
return { ...state, text: action.text };
default:
throw new Error();
}
}
export default function MultiState() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<input
type="text"
value={state.text}
onChange={e => dispatch({ type: 'setText', text: e.target.value })}
/>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
</div>
);
}
3. APIからのデータ取得
import React, { useReducer, useEffect } from 'react';
const initialState = { loading: true, data: null, error: null };
function reducer(state, action) {
switch (action.type) {
case 'fetchSuccess':
return { loading: false, data: action.data, error: null };
case 'fetchError':
return { loading: false, data: null, error: action.error };
default:
throw new Error();
}
}
export default function FetchDataComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch({ type: 'fetchSuccess', data });
} catch (error) {
dispatch({ type: 'fetchError', error: error.message });
}
};
fetchData();
}, []);
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error}</p>;
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(state.data, null, 2)}</pre>
</div>
);
}