axiosを使ってAPIコールしextraReducersで結果を処理しようとしていて、
もしAPIが失敗した場合、個別にエラーハンドリングを行うのではなく共通で行いたいと思いました。
400だったらアレしたい、401だったらアレしたいというふうに。
下記のコードのような実装です。
export const slice = createSlice({
...省略,
extraReducers: (builder) => {
builder.addCase(getMe.fulfilled, (state, action) => {
state.me = action.payload.me
})
builder.addMatcher(
(action) => action.type.endsWith("/rejected"),
handleApiRejected
)
},
})
export const handleApiRejected = (state: any, action: any) => {
console.log(action)
}
export const getMe = createAsyncThunk<{ me: Me }>("me", async () => {
const response = await axiosInstance.get("me")
return response.data
})
createAsyncThunkで例外が発生した場合、 xxx/rejected
みたいな /rejected
で終わるアクションがディスパッチされます。
それをextraReducerの builder.addMatcher
で捕まえることができます。
そこまではいいのですが、actionの内容は以下のようなものでした。
error: {
name: 'AxiosError',
message: 'Request failed with status code 401',
stack: 'AxiosError: Request failed with status code 401\n …/./node_modules/axios/lib/adapters/xhr.js:121:66)',
code: 'ERR_BAD_REQUEST'
}
meta: {arg: undefined, requestId: '41f2ZwUal7bRkxX1fQb-_', rejectedWithValue: false, requestStatus: 'rejected', aborted: false, …}
payload: undefined
type: "getProjects/rejected"
一見、十分なエラー内容に見えるがHTTP Status Codeが無い。
私のやりたかった、ステータスコードをもとにアレやコレをしたいということが実現できないではないか。
message
の文字列をパースすれば取れるじゃないかって?
はい、頑張ればできますがそんなことしたくないよね。
このエラーメッセージが作られてるのはどこや
https://github.com/axios/axios
の lib/core/settle.js
です。
export default function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus;
if (!response.status || !validateStatus || validateStatus(response.status)) {
resolve(response);
} else {
reject(new AxiosError(
'Request failed with status code ' + response.status,
[AxiosError.ERR_BAD_REQUEST, AxiosError.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4],
response.config,
response.request,
response
));
}
}
ふむふむ。
AxiosErrorの定義を見てみよう
lib/core/AxiosError.js
function AxiosError(message, code, config, request, response) {
Error.call(this);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = (new Error()).stack;
}
this.message = message;
this.name = 'AxiosError';
code && (this.code = code);
config && (this.config = config);
request && (this.request = request);
response && (this.response = response);
}
なるほど。AxiosErrorオブジェクトとしては this.response
として、レスポンスの元となる情報は持っているらしい。
じゃあReduxToolkit側で削ぎ落とされているのか
ビンゴです。
docs/api/createAsyncThunk.mdx
こちらを読むに
な、なんだって〜...
ここまでで察するに、axios側は、このインターフェースを知っていることになります。
たまたま「name, message, stack, code」という4つのプロパティが偶然に完全一致するとは思えません。
つまりaxios側が敢えて?HTTP Status Codeを数値のまま渡していないということになります。
多分そうなるよね。
その辺りの経緯まで調べるのは骨が折れそうなので諦めました。
結論
ということで、HTTP Status Codeを使って何かしらの分岐をしたい場合は他の方法を使いましょうということでした(雑な締め