Flux Standard Actionで失敗したアクションを識別する場合は、カスタムエラーオブジェクトを作れば良い

問題

ReduxなどのFluxアプリケーションの実装では、アクションのフォーマットとして、 Flux Standard Action(FSA) を採用しているプロジェクトも多いと思います。 FSAを使って非同期アクションの結果を表現しようとしているときに、以下のような疑問を抱いたことはないでしょうか?

たとえば、TODOアイテムの更新をするアクションとして、UPDATE_TODO:REQUESTED, UPDATE_TODO:RECEIVED という2つのアクションがあるとします。これらは、それぞれHTTPリクエストとレスポンスに対応します。

// リクエスト
{
    type: 'UPDATE_TODO:REQUESTED',
    payload: {
        id: 100,
        text: 'Do something!'
    }
}

// レスポンス
{
    type: 'ADD_TODO:RECEIVED',
    payload: {
        id: 100,
        text: 'Do something!'
    }
}

リクエスト成功時は、これで問題ありません。

では、エラーレスポンスが返ってきたときはどうなるでしょうか? FSAにおいては、error プロパティーが true の場合、payload はエラーオブジェクトであるべきと定められています。 JavaScriptのエラーオブジェクトは、 Errorコンストラクタ で表現されます。

{
    type: 'ADD_TODO:RECEIVED',
    payload: error, // error instanceof Error === true
    error: true
}

問題は、複数のTODOの更新操作が並行して走る場合です。リクエストエラー時には、ユーザーに対して操作が完了しなかったことを通知したいでしょう。複数の更新操作が同時に走るのであれば、どのアイテムに対する操作が失敗したのかも示したほうが親切かもしれません。

しかし、この場合、payloadError オブジェクトに占有されてしまっています。エラー時に、複数の操作を識別するためには、どうすれば良いでしょうか?

解法: カスタムエラーオブジェクトを作成する

ひとつの方法として、カスタムエラーオブジェクトを作成することが考えられます。

class IdentifiableError extends Error {
    constructor(id, ...args) {
        super(...args)
        this.name = 'IdentifiableError'
        this.targetId = id
    }
}

このように専用のエラークラスを定義し、リクエストエラー時には、このオブジェクトを payload に格納します。 エラーオブジェクトの targetId プロパティーを参照すれば、どの項目についての操作が失敗したのかを識別できます。 また、静的型環境での型付けも問題ありません。

別の解法1: エラー時専用のアクションを用意する

アクションとして、UPDATE_TODO:REQUESTEDUPDATE_TODO:RECEIVED の2種類ではなく、UPDATE_TODO:REQUESTEDUPDATE_TODO:SUCCEEDED , UPDATE_TODO:FAILED の3種類にします。 そして、FAILEDの場合には、error !== true の通常のアクションとして、payload に非エラーオブジェクトを乗せます。

別の解法2: metaに情報を持たせる

FSAでは、typepayloaderror 以外の第4のフィールドとして、meta プロパティーを持つことが許されています。 meta プロパティーにも payload と同様に任意のオブジェクトを乗せられるため、これを使って解決することも可能です。 ただ、いまここで乗せたい識別情報は、meta に乗せるような情報なのかは、判断が難しいところです。 更新対象のIDというのは、どちらかというと、ペイロードそのもののようにも思えます。

ミドルウェアで、アクションごとのユニークなIDを発行して、それをmetaに乗せて、TODOアイテムと関連付けた上でリクエストの状態を管理するというような、よりシステマチックで大掛りな方法も考えられるかもしれません。

参考リンク