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