LoginSignup
6
5

More than 5 years have passed since last update.

react-native, redux-saga, react-native-router-flux を使ったアプリ開発で誰もが(特にボク)一度はハマること

Posted at

機械学習や統計学で株価分析をしていたu-kanです。 株の自動化システム構築中に、「3ヶ月でアプリ作れないの? あ、もちろんiOSとAndroidで動くやつね。 ちなみにサーバー側も0から構築よろしく!」といったキラーパスが飛んできて、楽しく苦しくコーディングしています(泣)

でもね、

株よりも回収率良いから全然おっけーなんです(・∀・)

現金ですね〜 って、無駄話は辞めて先に進みます!笑

背景

redux-sagaを用いて非同期的にサーバーと通信、Instagram like なホームフィードを作ってるのですが、どうやらreact-native-router-fluxを使うと少々厄介なことが起きるようなので備忘録的に。

バージョン

"react-native": "^0.39.0",
"react": "15.4.1",
"axios": "^0.15.2",
"react-native-router-flux": "^3.37.0",
"react-redux": "^4.4.6",
"redux-actions": "^1.1.0",
"redux-saga": "^0.13.0",

ちなみにmac環境, Sierraです。まず、動かないコードを貼るので、なんで動かないのか考えて見ましょう(*・ω・)

なにが問題だったか

saga で 特定のactionをしてtakeしようとしていた。yieldを使って処理を一時的に止めているにも関わらず、なぜか処理は流れていって、勝手に次の処理が始まったりしていた。

もちろんyieldが壊れているとかもなく、普通にactionは呼ばれているらしい。

というか関連するパッケージが多すぎて、react-nativeの記事書くの難しいな。。

正常に動かないコード

actions/FeedActions.js
import { createAction } from 'redux-actions';

export const fetchItems = createAction(fetchItems);
export const fetchItemsSuccess = createAction(fetchItemsSuccess, payload => payload);
export const fetchItemsFail = createAction(fetchItemsFail, err => err);

Saga/index.js

import { fork, take, put, call } from 'redux-saga/effects';
import * as FeedActions from '../actions/FeedActions';

export function fetchFeedItems() {
  return (
    axios.get('https://rallycoding.herokuapp.com/api/music_albums')
      .then((res) => {
        const payload = res.data;
        return { payload };
      })
      .catch((err) => {
        return { err };
      })
  );
}


function* fetchItemsAsync() {
  while (true) {
    const action = yield take(FeedActions.fetchItems);
    const { payload, err } = yield call(fetchFeedItems, action.payload);
    if (payload && !err) {
      yield put(FeedActions.fetchItemsSuccess(payload));
    } else {
      yield put(FeedActions.fetchItemsFail(err));
    }
  }
}


export default function* rootSaga() {
  yield fork(fetchItemsAsync);
}

この後ちゃんと、render されるコンポーネントの方で、fetchItemsアクションを呼べています。

動くコード

actions/FeedActions.js
import { createAction } from 'redux-actions';

export const FETCH_ITEMS = 'FetchItems';
export const FETCH_ITEMS_SUCCESS = 'FetchItemsSuccess';
export const FETCH_ITEMS_FAIL = 'FetchItemsFail';

export const fetchItems = createAction(FETCH_ITEMS);
export const fetchItemsSuccess = createAction(FETCH_ITEMS_SUCCESS, payload => payload);
export const fetchItemsFail = createAction(FETCH_ITEMS_FAIL, err => err);

import { fork, take, put, call } from 'redux-saga/effects';
import * as FeedActions from '../actions/FeedActions';
import * as CommentActions from '../actions/CommentActions';

export function fetchFeedItems() {
  return (
    axios.get('https://rallycoding.herokuapp.com/api/music_albums')
      .then((res) => {
        const payload = res.data;
        return { payload };
      })
      .catch((err) => {
        return { err };
      })
  );
}

function* fetchItemsAsync() {
  while (true) {
    const action = yield take(FeedActions.FETCH_ITEMS);
    const { payload, err } = yield call(fetchFeedItems, action.payload);
    if (payload && !err) {
      yield put(FeedActions.fetchItemsSuccess(payload));
    } else {
      yield put(FeedActions.fetchItemsFail(err));
    }
  }
}

export default function* rootSaga() {
  yield fork(fetchItemsAsync);
}

この辺のエラーを防ぐには

簡単に言うと、redux-actionsを使ったとしても、定数の定義は外に出さないといけないということ。そうしないと、react-native-router-fluxだか、redux-actionsだかの仕様的に、毎回特定の値がどのactionsにも格納されているようなので、take で待ったところでこちらが意図しているようには動かない

解決策を3つにまとめるならば、

  1. redux-actionsでactionsを定義する際は、直接createActionsに文字列を渡さずに、別で定義したものを渡しておく。
  2. redux-sagaでactionsをtakeする際は、redux-actions、react-native-router-fluxで使えるやり方ではなく、先ほど定義した定数を渡すこと
  3. みんなはっぴーになる

です。ちょっと絡んでいるパッケージが多すぎてまとめるのが大変。現在動いてるパッケージ全部見せながらでやっと、うまく説明できるきがする。。

6
5
0

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
6
5