Reducer内でのAction Dispatchについて
ReactJS, Redux, Reducerにおける「Can I dispatch an action in reducer?」の日本語解説
Reduxの設計原則では、reducerは純粋関数として扱われます。つまり、入力(stateとaction)が同じであれば、出力(新しいstate)も常に同じであることが保証されます。
reducer内でactionをdispatchすると、以下のような問題が発生する可能性があります
- 無限ループ
reducer内でactionをdispatchすると、そのactionが再びreducerに渡され、再びactionをdispatchする可能性があります。これにより、無限ループが発生する可能性があります。 - 副作用
reducerは副作用のない関数であるべきです。actionをdispatchすることは副作用を引き起こす可能性があります。 - 予測不可能性
reducerの挙動が予測できなくなります。
actionのdispatchは通常、コンポーネントで行われます
- コンポーネントは、必要なactionをdispatchするためのdispatch関数をpropsとして受け取ります。
- dispatchされたactionは、Reduxストアに渡され、reducerによって新しいstateが計算されます。
Reducer内でのAction Dispatchについて:コード例
一般的なケース:避けるべき
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return stat e;
}
};
この例では、reducerは純粋関数として実装されており、actionをdispatchしていません。
特殊なケース:middlewareを使用
const thunkMiddleware = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(acti on);
};
const reducer = (state, action) => {
switch (action.type) {
case 'FETCH_DATA_SUCCESS':
return { ...state, data: action.payload };
default:
return state;
}
};
const store = createStore(reducer, applyMiddleware(thunkMiddleware));
store.dispatch(async dispatch => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });
});
この例では、thunk middlewareを使用して、非同期操作の後にactionをdispatchしています。しかし、このパターンは慎重に使用し、副作用や無限ループを避けるように設計する必要があります。
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
const Counter = () => {
const dispatch = useDispatch();
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
dispatch({ type: 'INCREMENT' });
};
const decrement = () => {
setCount(count - 1);
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button >
</div>
);
};
この例では、コンポーネントが直接dispatch関数を呼び出して、actionをdispatchしています。これにより、reducerは純粋関数として維持され、副作用や無限ループを避けることができます。
カスタムフックを使用する
import { useDispatch } from 'react-redux';
const useCounter = () => {
const dispatch = useDispatch();
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
dispatch({ type: 'INCREMENT' });
};
const decrement = () => {
setCount(count - 1);
dispatch({ type: 'DECREMENT' });
};
return { count, increment, decrement };
};
reactjs redux reducers