我有一个生成通话记录列表的远程API。这当然可以很长,所以我需要延迟它。从RN文档我收集到,最好的选择是VirtualizedList。但是文档非常缺乏。例如,它谈论项目键。我可以提供我自己的函数(使用日期/时间值),但是getItem prop仍然要求从0开始的数组索引。那么RN使用密钥是什么?
另一件事是,我打印出对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);
所以我的主要问题是,如何将所有这些放在一起延迟加载我的数据?
答案 0 :(得分:1)
我使用redux-saga解决了这个问题,它比redux-thunk好得多。 这是我的代码,经过轻微编辑:
行动和减少者:
users
实际加载数据:
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>
);
}
}
基本上我只是在我的商店中填充data []部分,让FlatList进行渲染所需的任何内容。