LoginSignup
3
4

More than 5 years have passed since last update.

React+Reduxでめんどうな状態管理なしで他のコンポーネントの機能を呼び出す

Last updated at Posted at 2017-06-19

はじめに

react+reduxで他のコンポーネントの機能を直接呼び出したい場合があると思います。例えば、Google Maps JavaScript APIやchart.jsのようなそれ自身ステートを持つようなライブラリの機能をラップするコンポーネントがあって、そのAPIを他のコンポーネントから呼び出す場合です。

react+reduxサイドでも各ライブラリのステートに相当するステートを独自に管理して、コンポーネントがそれを参照して適切にライブラリのAPIを呼び出すのが正攻法な気がしますが、冗長だし、ステートの同期をとる必要があるのでこの「適切に」というのがけっこう面倒です。

そこで、ひとつの解決策として呼び出したいAPIに対応する action をキューに積むという方法を試してみました。以下でコードを示しながら説明していきます。断片的なのでわかりにくいですが想像で補ってください。

action

通常のactionと同様に宣言します。

export function someAPI(args) {
    return {
        type: ActionTypes.SOME_API,
        args
    };
}

そして、もうひとつ action queue の末尾のひとつを削除するための action を宣言しておきます。

export function removeFromActionQueue() {
    return {
        type: ActionTypes.REMOVE_FROM_ACTION_QUEUE,
    };
}

state

stateの初期値に action_queue を空のarray として追加しておきます。

const initialState = {
...
    action_queue: [],
};

reducer

reducer では先程の action_queue の末尾に action そのものを追加してしまいます。actionそのものである必要性は特にはないんですが、簡単なのでそうしています。

redux のコンベンションに従ってaction_queue を直接操作するのではなくコピーしたものに追加して大元のstateにつけかえます。

const mainReducer = function(state = initialState, action) {
    switch (action.type) {
...
    case ActionTypes.SOME_API:
        {
            const action_queue = state.action_queue.concat(action);
            return Object.assign({}, state, {action_queue});
        }
...

そして removeFromActionQueue も同様に直接操作しないようにします。

...
    case ActionTypes.REMOVE_FROM_ACTION_QUEUE:
        {
            const action_queue = state.action_queue.slice(0, -1);
            return Object.assign({}, state, {action_queue});
        }
...

component

実際にapiを呼び出すコンポーネントは action_queueをプロパティとして取り込みます。componentWillUpdateやcomponentDidUpdate等でその変化をつかまえて、末尾のactionを参照し、自分の管轄だったら、それに対応したAPIを呼び出し、そのあとremoveFromActionQueueを呼び出します。

以下のようなコードになります。

class MyComponent extends Component {
    componentDidUpdate() {
        const action = this.props.action_queue[len-1];
        if (action.type == ActionTypes.SOME_API) {
            call_api(action.args); // API を呼び出す
            this.props.removeFromActionQueue();
        }
    }
....
}

function mapStateToProps(state) {
    return {
        action_queue: state.main.action_queue,
...
    };
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({removeFromActionQueue, ...}, dispatch);
}

まとめ

われながらreduxの流儀からぎりぎりはずれないスマートな実装な気がします。少し前に書いたコードなので今はもっといい方法があるかもしれません。こっちの方がスマートという方法があれば教えてください。

3
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4