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


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

でもね、

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

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

## 背景

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 で待ったところでこちらが意図しているようには動かない

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

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

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