機械学習や統計学で株価分析をしていた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の記事書くの難しいな。。
正常に動かないコード
import { createAction } from 'redux-actions';
export const fetchItems = createAction(fetchItems);
export const fetchItemsSuccess = createAction(fetchItemsSuccess, payload => payload);
export const fetchItemsFail = createAction(fetchItemsFail, err => err);
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アクションを呼べています。
動くコード
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つにまとめるならば、
- redux-actionsでactionsを定義する際は、直接createActionsに文字列を渡さずに、別で定義したものを渡しておく。
- redux-sagaでactionsをtakeする際は、redux-actions、react-native-router-fluxで使えるやり方ではなく、先ほど定義した定数を渡すこと
- みんなはっぴーになる
です。ちょっと絡んでいるパッケージが多すぎてまとめるのが大変。現在動いてるパッケージ全部見せながらでやっと、うまく説明できるきがする。。