React
redux-thunk

Reactで非同期に現地を取得する

概略

ReactとReduxとFirebaseを使ったアプリを書いているのですが,このたび 任意のタイミングで現在地を取得 することになりました.
現在地の取得には時間がかかるので非同期処理として扱うべきなので,今回はReact+Redux環境でよく使われるというredux-thunkの導入とその利用法について書きます.

導入

yarnを使ってredux-thunkを入れましょう.npmでもflatにインストールできるようになって久しいですが,yarnの方が高速で更にセキュアに入れてくれます(参考: npm vs yarn).

yarn add react-thunk

利用法

storeを生成しているところで,以下のようにReduxと混ぜて使います.

index.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';

const store = createStore(
  reducer,
  applyMiddleware(thunk),
);

次に,アクションに非同期処理を書いていきます.
...が,Storeを弄るコードと混ざって見づらくなるので,middlewareに本命の非同期処理を書いてactionから呼ぶようにしました.

actions/index.js
import { getPosition } from '../middlewares';

export const getPresentLocation = () => {
  return async dispatch => {
    try {
      const position = await getPosition(); //middlewareで非同期処理
      await setPresentLocation(position); //他のアクションでstoreに結果を保存
    } catch(e) {
      alert(e)
    }
  }
};
middlewares/index.js
export const getPosition = () => new Promise(resolve, reject) => {
  if(!navigator.geolocation) {
    reject("このブラウザは現在地の取得に対応していません:(");
  }

  navigator.geolocation.getCurrentPosition(position => resolve({
    lat: position.coords.latitude,
    lng: position.coords.longitude,
  }), e => reject(`エラー(${e.code}): ${e.message}`));
};

あとは,Storeを弄るのと同じようにdispatchでその非同期処理を行っている関数を呼ぶだけです.

components/main.js
import React from 'react';
import getPresentLocation from '../actions';

const main = ({ dispatch }) => (
  <button onClick={dispatch(getPresentLocation())}>
    現在地取得!
  </button>
);

export default connect()(main);