2017-06-28 6 views
0

私は、コール履歴リストを生成するリモートAPIを持っています。もちろん、これはかなり長くなる可能性があるので、私はそれをlazyloadする必要があります。 RNのドキュメントから、私はVirtualizedListになることが最善の選択であることを知ります。しかし、ドキュメントはひどく欠けています。たとえば、項目キーについて説明します。 (日付/時刻の値を使用して)独自の関数を提供することはできますが、getItemの小道具は依然として0から始まる配列インデックスを要求しています。リモートデータを使ってVirtualizedListを実装する方法は?

もう1つは、getItemとrenderItemの呼び出しを印刷していて、私は本当に奇妙なパターン(私はinitialNumToRenderとmaxToRenderPerBatchを両方とも13に設定しています)です。これはアプリ起動時であり、ユーザーの操作はありません。また、私のgetItemCountは15を返す:

VirtualizedList.render() call 
getItem 13 times: 0-12 
getItem 0 
renderItem 13 times: 0-12 
VirtualizedList.render() call 
getItem 13 times: 0-12 
getItem 12 
getItem 0 
renderItem 12 times: 0-12 
getItem 0 
getItem 12 
VirtualizedList.render() call 
getItem 13 times: 0-12 
getItem 12 
getItem 0 
renderItem 12 times: 0-12 
getItem 0 
getItem 12 
getItem 0 
getItem 12 
(10 more like the 2 repeating above) 
getItem 0-12 
getItem 1 
getItem 2 
getItem 3 
getItem 4 
getItem 5 
getItem 6 
getItem 9 
getItem 10 
getItem 12 (Skipping some items here???) 
onViewableItemsChanged, info= Object {viewableItems: Array(9), changed: Array(9)} 
getItem 0-14 
getItem 0-14 
renderItem 0-14 
onEndReached, info= Object {distanceFromEnd: 93.5} (what is that value 93.5????) 
getItem 0-12 
getItem 0-11 
onViewableItemsChanged, info= Object {viewableItems: Array(12), changed: Array(5)} 
getItem 0-14 
onEndReached, info= Object {distanceFromEnd: 221???} 
getItem 0-11 
getItem 0-10 
onViewableItemsChanged, info= Object {viewableItems: Array(11), changed: Array(1)} 
getItem 0-14 

私はまだ画面に触れていないことに注意してください。それは私がスクロールし、画素毎に思わ

getItem 0-12 
(repeats for around 20 times) 
onViewableItemsChanged, info= Object {viewableItems: Array(12), changed: Array(1)} 
getItem 0-12 
(repeats for around 20 times) 

は、すべてのアイテムが取得されています。私は少し上にスクロールするとき今、私は次のイベントを取得します。参考のため

は、ここに私のコードは次のとおりです。

import Expo from 'expo'; 
import React, { PureComponent } from 'react'; 
import { Platform, FlatList, VirtualizedList, View, StyleSheet, Text } from 'react-native'; 
import { combineReducers } from 'redux'; 
import { ListItem } from 'react-native-elements'; 
import { connect } from 'react-redux'; 
import I18n from '../i18n'; 
import { takeEvery, all, call, put, select } from 'redux-saga/effects'; 
import RecentRow from '../components/RecentRow'; 
import { getUserId } from './Settings'; 
import { AppText, AppHeaderText } from '../components/AppText'; 

// action types 
const RECENT_LOAD = 'RECENT_LOAD'; 
const RECENT_LOAD_OK = 'RECENT_LOAD_OK'; 
const RECENT_LOAD_ERROR = 'RECENT_LOAD_ERROR'; 

// action functions 
export function recentLoad(offset) { 
    return { 
    type: RECENT_LOAD, 
    offset: offset, 
    }; 
} 

// reducers 
function recent(state = { offset: 1, data: [] }, action) { 
    //console.log('recent', action); 
    switch (action.type) { 
    case RECENT_LOAD: 
     return { 
     ...state, 
     offset: action.offset 
     }; 

    case RECENT_LOAD_OK: 
     return { 
     ...state, 
     data: action.data, 
     }; 
    default: 
     return state; 
    } 
} 

// combined reducer 
export const recentList = combineReducers({ 
    recent: recent, 
}); 

export const getRecent = state => state.recent; 
export const getAccount = state => state.settings.account; 

function* recentLoadData(action) { 
    const account = yield select(getAccount); 
    const URL = `https://www.xxxxx.xx/api/calls.php?userrname=${account.email}&offset=${action.offset}`; 
    try { 
    const response = yield call(fetch, URL); 
    if (response.status === 200) { 
     result = yield call([response, 'json']); 
     yield put({ type: RECENT_LOAD_OK, data: result }); 
    } else { 
     yield put({ type: RECENT_LOAD_ERROR, error: response.status }); 
    } 
    } 
    catch(error) { 
    console.log('error:', error); 
    yield put({ type: RECENT_LOAD_ERROR, error: error }) 
    } 
} 

function* recentLoadSaga() { 
    yield takeEvery('RECENT_LOAD', recentLoadData); 
} 

export function* recentSaga() { 
    yield all([ 
    recentLoadSaga(), 
    ]) 
} 

class RecentList extends PureComponent { 
    componentDidMount() { 
    this.props.loadRecentCalls(); 
    } 

    _renderItem = (item, userid) => { 
    console.log('_renderItem', item); 
    //return <RecentRow row={item} userid={userid} /> 
    return <ListItem title={item.item.name + ' ' + item.item.id } /> 
    } 

    renderSeparator =() => { 
    return (
     <View 
     style={{ 
      height: 1, 
      width: "95%", 
      backgroundColor: "#CED0CE", 
      marginLeft: "5%" 
     }} 
     /> 
    ); 
    }; 

    render() { 
    console.log('RecentList.render()'); 
    return (
     <View style={styles.container}> 
     <View style={styles.lineitem}> 
      <View style={styles.header}> 
      <AppHeaderText>{I18n.t('calls')}</AppHeaderText> 
      </View> 
     </View> 
     <VirtualizedList 
      data={this.props.recent.data} 
      extraData={this.props} 
      keyExtractor={item => item.somekey} 
      renderItem={(item) => this._renderItem(item, this.props.userid)} 
      initialNumToRender="13" 
      maxToRenderPerBatch="13" 
      //ItemSeparatorComponent={this.renderSeparator} 
      ListEmptyComponent={() => { 
      return (
       <View style={styles.centerScreen}> 
       <View> 
        <AppText>{I18n.t('nocallsfound')}</AppText> 
       </View> 
       </View> 
      ) 
      }} 
      ListFooterComponent={() => { 
      return (
       <Text>Footer goes here</Text> 
      ) 
      }} 
      ListHeaderComponent={() => { 
      return (
       <Text>Header goes here</Text> 
      ) 
      }} 
      getItem={ (data, index) => { 
      console.log('getItem', index); 
      return {name: 'My Name', id: index, somekey: index+1000}; 
      }} 
      getItemCount={ (data, index) => { 
      //console.log('getItemCount'); 
      return 15; 
      }} 
      onEndReached={ (info) => { 
      console.log('onEndReached, info=', info); 
      }} 
      onViewableItemsChanged={ (info) => { 
      console.log('onViewableItemsChanged, info=', info); 
      }} 
      /> 
     </View> 
    ); 
    } 
} 

const styles = StyleSheet.create({ 
    container: { 
    flex: 1, 
    flexDirection: 'column', 
    justifyContent: 'flex-start', 

    backgroundColor: 'whitesmoke', 
    }, 
    header: { 
    flex: 1, 
    flexDirection: 'row', 
    justifyContent: 'center', 
    alignItems: 'center', 

    borderColor: 'grey', 
    borderBottomWidth: 1, 
    height: 40, 
    }, 
    lineitem: { 
    flexDirection: 'row', 
    justifyContent: 'space-between', 
    alignItems: 'center', 

    backgroundColor: 'white', 
    padding: 5, 
    }, 
    centerScreen: { 
    flex: 1, 
    flexDirection: 'column', 
    justifyContent: 'center', 
    alignItems: 'center', 
    height: 300, 
    } 
}); 

const mapStateToProps = (state, props) => { 
    return { 
    recent: state.recentList.recent, 
    userid: getUserId(state), 
    }; 
}; 

const mapDispatchToProps = (dispatch, props) => { 
    return { 
    loadRecentCalls:() => dispatch(recentLoad(0)), 
    }; 
}; 

export default connect(mapStateToProps, mapDispatchToProps)(RecentList); 

だから私のメインの質問は、私は私のデータを遅延読み込みすべて一緒にこれを置けばいいのか、ですか?

答えて

1

私はこれをredux-sagaを使って解決しました。これはredux-thunkよりはるかに優れています。

アクションとレデューサー:

const LOAD = 'LOAD'; 
const LOAD_OK = 'LOAD_OK'; 
const LOAD_ERROR = 'LOAD_ERROR'; 
const REFRESH_START = 'REFRESH_START'; 

export function mylistRefreshStart() { 
    return { 
    type: REFRESH_START, 
    append: false, 
    }; 
} 

export function mylistLoad() { 
    return { 
    type: LOAD, 
    append: true, 
    }; 
} 

// reducer 
export const mylist = (state = { offset: 0, limit: 50, data: [], refreshing: true }, action) => { 
    //console.log('mylist:', action); 
    switch (action.type) { 
    case REFRESH_START: 
     return { 
     ...state, 
     refreshing: true, 
     offset: 0, 
     limit: 50, 
     }; 

    case LOAD_OK: 
     return { 
     ...state, 
     data: action.append ? state.data.concat(action.data) : action.data, 
     refreshing: false, 
     limit: action.data.length !== 50 ? 0 : 50, 
     }; 

    case LOAD_ERROR: 
     return { 
     ...state, 
     refreshing: false, 
     }; 

    default: 
     return state; 
    } 
}; 

// selector 
export const getMyData = state => state.mylist; 

は、実際にデータをロードする:

function* mylistLoadData(action) { 
    const mylist = yield select(getMyData); 
    if (mylist.limit === 0) { 
    //console.log('nothing left to fetch'); 
    return; 
    } 
    try { 
    const response = yield call(fetch, 'https://www.example.com/api/mylist.php', { 
     method: 'post', 
     headers: { 
     'Accept': 'application/json', 
     'Content-Type': 'application/json', 
     }, 
     body: JSON.stringify({ 
     offset: action.append ? mylist.offset + mylist.data.length : mylist.offset, 
     limit: mylist.limit, 
     }), 
    }); 
    if (response.status === 200) { 
     result = yield call([response, 'json']); 
     yield put({ type: LOAD_OK, data: result, append: action.append }); 
    } else { 
     yield put({ type: LOAD_ERROR, error: response.status }); 
    } 
    } 
    catch(error) { 
    console.log('error:', error); 
    yield put({ type: LOAD_ERROR, error: error }) 
    } 
} 

すべての処理を扱い佐賀:

export function* mylistSaga() { 
    yield takeLatest(REFRESH_START, mylistLoadData); 
    yield takeLatest(LOAD, mylistLoadData); 
} 

レンダリングここ は私のコードは、軽く編集します:

class MyList extends PureComponent { 
    componentDidMount =() => { 
    this.props.refreshStart(); 
    }; 

    onRefresh =() => { 
    this.props.refreshStart(); 
    }; 

    onEndReached =() => { 
    this.props.mylistLoad(); 
    }; 

    render =() => { 
    return (
     <View style={styles.container}> 
     <FlatList 
      data={this.props.mylist.data} 
      extraData={this.props} 
      keyExtractor={item => item.id} 
      refreshing={this.props.recent.refreshing} 
      renderItem={(item) => this._renderItem(item)} 
      ListEmptyComponent={() => { 
      if (this.props.mylist.refreshing) return null; 
      return (
       <View style={styles.centerScreen}> 
       <View> 
        <Text>Nothing found</Text> 
       </View> 
       </View> 
      ) 
      } 
      } 
      onRefresh={() => this.onRefresh()} 
      onEndReached={() => this.onEndReached()} 
      /> 
     </View> 
    ); 
    } 
} 

とアクションを結ぶ:

const mapStateToProps = (state, props) => { 
    return { 
    mylist: state.mylist, 
    }; 
}; 

const mapDispatchToProps = (dispatch, props) => { 
    return { 
    refreshStart:() => dispatch(recentRefreshStart()), 
    mylistLoad:() => dispatch(mylistLoad()), 
    }; 
}; 

export default connect(mapStateToProps, mapDispatchToProps)(MyList); 

基本的に私はちょうど私の店でのデータ[]の部分を埋めています、とFlatListを示すことが必要とされているもののレンダリングをやってみましょう。

関連する問題