VuexやPiniaで非同期処理を扱っている際、APIのレスポンスをストアに反映させようとして「TypeError: Cannot read properties of undefined」が発生したり、状態が意図通りに更新されなかったりすることはありませんか?
私も先日この挙動でハマったので、備忘録として解決策をまとめます。
目次
この記事でわかること
- 非同期処理後のコミットで発生するエラーの主な原因
- ペイロード構造の不一致を防ぐための実装手法
- Vuex/Piniaにおける堅牢なエラーハンドリングの書き方
[現象] 状態が更新されない、またはundefinedエラーが発生
非同期処理(APIリクエストなど)を実行し、その結果を commit または patch した直後に以下のようなエラーが発生する、あるいはエラーは出ないもののStateが null や undefined のまま更新されないという現象です。
TypeError: Cannot read properties of undefined (reading 'someProperty')
または
Store state not updating as expected after dispatching action.
[環境]
- フレームワーク: Vue.js 2.x / 3.x, Nuxt.js
- 状態管理ライブラリ: Vuex 3.x / 4.x または Pinia
- 通信ライブラリ: Axios / Fetch API
[原因] ペイロード形式の不一致と不十分なバリデーション
主な原因は、アクション(Action)内で取得したデータ構造と、ミューテーション(Mutation)が期待するデータの型が一致していないことにあります。
特に以下のケースで頻発します。
| 原因の分類 | 具体的な内容 |
|---|---|
| 構造の不一致 | APIレスポンスの response.data をそのまま渡しているが、ミューテーション側は { user: ... } のようなラップされた構造を期待している。 |
| 非同期の考慮不足 | awaitを忘れている、またはPromiseが解決される前に状態を参照しようとしている。 |
| 未定義データの放置 | APIが200 OKを返しても、 中身のデータが空(null)であるケースをハンドリングしていない。 |
[解決策] 正確なペイロード構築とエラーハンドリング
解決のためには、ミューテーションにデータを渡す前に必ずデータ構造をチェックし、必要なプロパティのみを抽出して渡すように修正します。
修正コード (Before / After)
// Before (問題のあるAction)
async commitLoginResult({ commit }) {
try {
const response = await api.login(credentials);
// レスポンスの構造を検証せずにそのままコミットしている
commit('SET_USER_DATA', response.data.user);
} catch (error) {
commit('SET_ERROR', 'Login failed');
}
}
// After (改善されたAction)
async commitLoginResult({ commit }) {
try {
const response = await api.login(credentials);
// 1. データの存在チェックを徹底する
if (response && response.data && response.data.user) {
// 2. ミューテーションが期待する形式に整形して渡す
commit('SET_USER_DATA', {
id: response.data.user.id,
name: response.data.user.name,
// 必要なプロパティを明示的に指定
});
} else {
// データが不完全な場合のハンドリング
commit('SET_ERROR', 'ユーザーデータが見つかりませんでした。');
}
} catch (error) {
// 3. エラー内容をコンソールに出力し、ユーザーにも通知する
console.error('Login API error:', error);
commit('SET_ERROR', 'ログイン処理中に予期しないエラーが発生しました。');
}
}
また、ミューテーション側も受け取る引数が正しいか確認しておきましょう。
mutations: {
SET_USER_DATA(state, userData) {
// state.userが確実にuserDataオブジェクトになるようにする
state.user = userData;
},
SET_ERROR(state, errorMessage) {
state.error = errorMessage;
}
}
[まとめ]
VuexやPiniaを用いた非同期処理のポイントは、「APIからの生データをそのまま信用しない」ことです。アクション内でしっかりとバリデーションを行い、正しい形式でコミットすることで、 undefined 起因のバグを劇的に減らすことができます
。非同期処理とエラーハンドリングを適切に組み合わせて、堅牢なフロントエンドを構築しましょう!

