Redux の getState() で状態が取得できない問題と解決策
問題の概要
Redux において createStore() が非推奨となり、代わりに @reduxjs/toolkit の configureStore() の使用が推奨されています。この変更に伴い、アクション内で getState() を使用して状態を取得しようとすると、以下のような問題が発生することがあります:
export const addProduct = product => async (dispatch, getState) => {
try {
const { userLogin: { userInfo } } = getState() // undefined が返される
// ...
} catch (error) {
// エラーハンドリング
}
}getState() から userInfo が undefined として返され、認証トークンの取得に失敗します。しかし、getState() を削除するとアクションは正常に動作します。
根本原因
この問題は createStore() の非推奨化自体が直接の原因ではありません。Redux メンテナーによると、createStore の非推奨表示はエディター上の視覚的な指標であり、ランタイムの警告ではないと説明されています。
実際の問題は、ストアの設定方法とミドルウェアの設定に関連しています。
解決策
1. Redux Toolkit の正しい設定方法
configureStore を使用する際は、ミドルウェアの設定を適切に行う必要があります:
import { configureStore } from '@reduxjs/toolkit'
import thunk from 'redux-thunk'
// 各Reducerのインポート
const reducer = {
userLogin: userLoginReducer,
// その他のReducer
}
const userInfoFromStorage = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const preloadedState = {
userLogin: { userInfo: userInfoFromStorage },
}
const store = configureStore({
reducer,
preloadedState, // 正しいスペル: preloadedState (not preLoadedState)
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(thunk),
})
export default store注意
preLoadedState ではなく preloadedState(Lが小文字)が正しいプロパティ名です。このスペルミスが状態の初期化に影響を与える可能性があります。
2. モダンな Redux への移行
Redux Toolkit を使用したより現代的なアプローチ:
// store.js
import { configureStore } from '@reduxjs/toolkit'
import { userLoginReducer } from './reducers/userReducers'
export const makeStore = () => {
return configureStore({
reducer: {
userLogin: userLoginReducer,
// その他のReducer
},
preloadedState: {
userLogin: {
userInfo: localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
}
},
// middlewareは自動的に設定される(thunkを含む)
})
}3. createStore のレガシーな使用法(非推奨)
どうしても従来の createStore を使用する必要がある場合:
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(
reducer,
preloadedState,
composeWithDevTools(applyMiddleware(thunk))
)非推奨
この方法はレガシーなアプローチであり、新規プロジェクトでは推奨されません。Redux Toolkit を使用することが公式推奨です。
ベストプラクティス
モダンなアクションの書き方
Redux Toolkit では createAsyncThunk を使用することで、より簡潔に非同期アクションを記述できます:
import { createAsyncThunk } from '@reduxjs/toolkit'
export const addProduct = createAsyncThunk(
'products/add',
async (product, { getState }) => {
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
}
const response = await axios.post('/product', product, config)
return response.data
}
)ストア設定の完全な例
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './userSlice'
import productReducer from './productSlice'
const store = configureStore({
reducer: {
user: userReducer,
product: productReducer,
},
preloadedState: {
user: {
userInfo: localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
}
},
devTools: process.env.NODE_ENV !== 'production',
})
export default store結論
getState() が undefined を返す問題は、主に以下の原因が考えられます:
- ストア設定の不備:
preloadedStateのスペルミスや不適切な設定 - ミドルウェア設定の問題:thunk ミドルウェアが正しく設定されていない
- 状態構造の不一致:
getState()で想定している状態構造と実際の構造が異なる
Redux Toolkit の configureStore を使用し、公式ドキュメントに従った適切な設定を行うことで、これらの問題を解決できます。新しいプロジェクトでは、常に Redux Toolkit を採用し、モダンな Redux パターンを実践することをお勧めします。