前提
React Native + ReduxでGitHubのリポジトリを検索できるアプリを作成しています。
非同期処理の部分でredux-sagaを使用しています。
想定している流れとしては以下の通りです。
- SearchBarにテキストを入力するたびにonChangeTextでactionをdispatchする。(問題ない)
- Middlewareで 1 の時にdispatchされるactionを監視する(問題あり)
- APIを叩いて取得したリポジトリを包んでreducerにsuccessかfailのactionをdispatch(問題あり)
- reducerで新たなstateを作成してstoreを更新(問題ありかも)
- FlatListでstateの状態を監視してリポジトリの状態が変わったら、UIに反映させる(問題ありかも)
ゴール
SearchBarで onChnageTextのたびにAPIを叩いてリポジトリを取得し、FlatListに表示する。
※ Javascirpt初心者のため記法など雑な部分が散見されますがご容赦頂ければ幸いです。
現状の把握と問題点の仮説
ここまでで確認したことを簡潔にまとめると
・ ActionCreatorは問題なく機能している(ActionはDispatchされている)
です。テキスト入力のたびにdispatchされるactionは想定ではmiddlewareで止まり、reducerには流れません。
middlewareではsuccessかfailの別のactionをdispatchするようにしています。
しかし、実際にはreducerにテキスト入力のたびにdispatchされるactionが流れています。
(一時的にreducerにテキスト入力のたびにdispatchされるactionのcaseを追加して確かめました)
また、前提の 4,5 を問題ありかもにしたのはreducerで initialState
に適当なオブジェクトを入れてもFlatListに反映されなかったためです。
大きく分けて
・ 前提における 2, 3 が問題。つまり redux-saga の扱いに問題がある。
・ 前提における 4, 5 が問題。つまり ReducerとStore、Componentの連携に問題がある。
のどちらかだと考えています。
解決の糸口になるようなアドバイスを頂ければ幸いです。
コード
- Component(問題となるFlatListを使用しているComponent)
このアプリではSearchBarの onChangeText と連動してリアルタイムでAPIを叩いています。
そのため、表示する責務だけを担う Repo_FlatList
では makeStateToProps
だけを指定し、あえて makeDispatchToProps
は null
にしています。
import React, {Component} from 'react'; import {View, Text, FlatList} from 'react-native'; import {connect} from 'react-redux'; export class Repo_FlatList extends Component { render() { return( <FlatList data={this.props.repos} renderItem={({repo}) => <View style={styles.cell}> <Text style={styles.text} >{repo.name}</Text> </View>} /> ) } } const styles = { cell: { flexDirection: 'row', borderStyle: 'solid', borderWidth: 0.5, borderColor: '#bbb', }, text: { padding: 10, fontSize: 18, }, } const mapStateToProps = state => ({ repos: state.responseHandler.repos }) export default connect(mapStateToProps, null)(Repo_FlatList);
- Component(SearchBarの部分)
import React, {Component} from 'react'; import {SearchBar} from 'react-native-elements'; import {bindTextChangedAction} from '../actions/ui_action'; import {fetchAction} from '../actions/model_action'; import {connect} from 'react-redux'; export class Repo_SearchBar extends Component { render() { return( <SearchBar round placeholder="Search" onChangeText={text => {this.props.bindTextChangedAction(text); this.props.fetchAction(text)}} value={this.props.searchText} /> ) } } const mapStateToProps = state => ({ searchText: state.bindTextChanged.searchText }) const mapDispatchToProps = { bindTextChangedAction,fetchAction } export default connect(mapStateToProps, mapDispatchToProps)(Repo_SearchBar);
- ActionCreator(fetchの部分)
import {FETCH_REPOSITORY, FETCHREQUEST_SUCCESS, FETCHREQUEST_FAILE} from "../constants/constants" export const fetchAction = text => { return {type: FETCH_REPOSITORY, payload: text} } export const requestSuccess = repos => { return {type: FETCHREQUEST_SUCCESS, payload: repos} } export const requestFaile = e => { return {type: FETCHREQUEST_FAILE, payload: e.message} }
- reducer
import { FETCHREQUEST_FAILE, FETCHREQUEST_SUCCESS, FETCH_REPOSITORY } from "../constants/constants"; const initialState = { repos: [] } const responseHandler = (state = initialState, action) => { switch(action.type) { case FETCHREQUEST_SUCCESS: return Object.assign({}, state, {repos: action.payload}); case FETCHREQUEST_FAILE: return Object.assign({}, state, {repos: action.payload}); default: return state } } export default responseHandler;
- Middelware(fetchする部分)
import {call, put, takeEvery, take} from 'redux-saga/effects'; import {FETCH_REPOSITORY} from '../constants/constants'; import {requestSuccess, requestFaile} from '../actions/model_action'; function* fetchRepos(action) { while(true) { const action = yield take(FETCH_REPOSITORY) const text = action.payload try { const repos = yield call(fetchAsync, text) yield put(requestSuccess(repos)) } catch(e) { yield put(requestFaile(e.message)) } } } function* fetchAsync(text) { return fetch(`https://api.github.com/search/repositories?q=${text}+in:name&sort=stars`) .then(response => response.json(JSON.parse)) .then(json => JSON.stringify(json)) .catch((e) => { throw(e)}) } export default fetchRepos;
- Middelware(root)
import { all } from 'redux-saga/effects'; import {fetchRepos} from './watchFetchAction'; export default function* rootSaga() { yield all([fetchRepos]) }
- Store
import { createStore, combineReducers, applyMiddleware} from "redux"; import createSagaMiddleware from 'redux-saga'; import bindTextChanged from './src/reducers/ui_reducer'; import responseHandler from './src/reducers/model_reducer'; import fetchRepos from './src/middleware/watchFetchAction'; const rootReducer = combineReducers({bindTextChanged, responseHandler}); const configureStore = () => { const sagaMiddleware = createSagaMiddleware(); const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); sagaMiddleware.run(fetchRepos); return store } export default configureStore;
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/10/27 05:49
2019/10/27 07:10