As you can see in the picture above, there is a vertical list of two columns. I have developed two flatLists because I can not style photos with different height on one line with the flatList column option.
I want two flatLists to have the same scroll position.
I wrapped two FlatList component with to synchronize scrolling. But there is a problem here. The scrollView ignores the infinity scroll and calls the pagination objects all at once. There is a performance problem.
How can I solve this problem?
here is my component code
import React from 'react';import { View, Platfrom, Text, StyleSheet, AsyncStorage, TouchableOpacity, Image, FlatList, Button, Dimensions, ScrollView, ListView } from 'react-native';import Icon from 'react-native-vector-icons/FontAwesome';import { NavigationActions } from 'react-navigation';import { connect } from 'react-redux';import moment from 'moment';import * as settings from '../../config/settings';import Menu from '../menu';import headerStyles from '../../styles/common/header';import textStyles from '../../styles/common/text';import containerStyles from '../../styles/common/container';import imageStyles from '../../styles/common/image';import boxStyles from '../../styles/common/box';import objectStyles from '../../styles/common/object';import commonStyles from '../../styles/common/common';import colorStyles from '../../styles/common/color';const window = Dimensions.get('window');const styles = StyleSheet.create({ listView: { paddingTop: 20, backgroundColor: '#FFFFFF', }, fullScreen: { flex: 1, marginBottom: 50, }, floatView: { position: 'absolute', width: '90%', marginLeft: '5%', height: 100, bottom: -50, },});const phoneWidth = Dimensions.get('window').widthclass PurchaseListScreen extends React.Component { static navigationOptions = ({ navigation }) => { const { params = {} } = navigation.state return { title: '중고명품구매', headerStyle: headerStyles.header, headerTitleStyle: headerStyles.headerTitle, headerRight: <Icon name="bars" size={30} color="#333" onPress={() => { params.showModal() }} style={{ padding: 10 }} />, } } constructor(props) { super(props); this.state = { user_token: '', refreshing: false, isModalVisible: false, leftColumneData: [], rightColumneData: [], leftNextKey: '', rightNextKey: '', }; this.hideModal = this.hideModal.bind(this); } componentDidMount() { this.props.navigation.setParams({ showModal: this.showModal }); this.purchaseListService(); } showModal = () => this.setState({ isModalVisible: true }) hideModal = () => this.setState({ isModalVisible: false }) navigate1 = (item) => { console.log('click navigate1'); const navigate1 = NavigationActions.navigate({ routeName: "home", }); this.props.navigation.dispatch(navigate1); }; purchaseListService = async () => { if (this.props.isLoggedIn) { let user_info = await AsyncStorage.getItem('user_info'); get_user_token = JSON.parse(user_info).key; this.setState({ user_token: get_user_token }); const api_uri1 = settings.base_uri +'purchase-product/odd/' const request1 = { method: 'GET', headers: {'Authorization': 'Token '+ get_user_token,'Accept': 'application/json','Content-Type': 'application/json', } } const api_uri2 = settings.base_uri +'purchase-product/even/' const request2 = { method: 'GET', headers: {'Authorization': 'Token '+ get_user_token,'Accept': 'application/json','Content-Type': 'application/json', } } fetch(api_uri1, request1) .then(res => res.json()) .then(res => { this.setState({ leftColumneData: [...res.results], leftNextKey: res.next }) }) .catch((error) => { console.error(error); }) fetch(api_uri2, request2) .then(res => res.json()) .then(res => { this.setState({ rightColumneData: [...res.results], rightNextKey: res.next, }) }) .catch((error) => { console.error(error); }) } else { const api_uri1 = settings.base_uri +'purchase-product/odd/' const request1 = { method: 'GET', headers: {'Accept': 'application/json','Content-Type': 'application/json', } } const api_uri2 = settings.base_uri +'purchase-product/even/' const request2 = { method: 'GET', headers: {'Accept': 'application/json','Content-Type': 'application/json', } } fetch(api_uri1, request1) .then(res => res.json()) .then(res => { this.setState({ leftColumneData: [...res.results], leftNextKey: res.next, }) }) .catch((error) => { console.error(error); }) fetch(api_uri2, request2) .then(res => res.json()) .then(res => { this.setState({ rightColumneData: [...res.results], rightNextKey: res.next, }) }) .catch((error) => { console.error(error); }) } } onEndReachedService = () => { console.log('run onEndReachedService'); if (this.props.isLoggedIn && this.state.leftNextKey) { const api_uri1 = this.state.leftNextKey const request1 = { method: 'GET', headers: {'Authorization': 'Token '+ this.state.user_token,'Accept': 'application/json','Content-Type': 'application/json', } } const api_uri2 = this.state.rightNextKey const request2 = { method: 'GET', headers: {'Authorization': 'Token '+ this.state.user_token,'Accept': 'application/json','Content-Type': 'application/json', } } fetch(api_uri1, request1) .then(res => res.json()) .then(res => { this.setState({ leftColumneData: [...this.state.leftColumneData, ...res.results], leftNextKey: res.next }) }) .catch((error) => { console.error(error); }) fetch(api_uri2, request2) .then(res => res.json()) .then(res => { this.setState({ rightColumneData: [...this.state.rightColumneData, ...res.results], rightNextKey: res.next, }) }) .catch((error) => { console.error(error); }) } else if (!this.props.isLoggedIn && this.state.leftNextKey){ const api_uri1 = this.state.leftNextKey const request1 = { method: 'GET', headers: {'Accept': 'application/json','Content-Type': 'application/json', } } const api_uri2 = this.state.rightNextKey const request2 = { method: 'GET', headers: {'Accept': 'application/json','Content-Type': 'application/json', } } fetch(api_uri1, request1) .then(res => res.json()) .then(res => { console.log('update res1', res); this.setState({ leftColumneData: [...this.state.leftColumneData, ...res.results], leftNextKey: res.next, }) }) .catch((error) => { console.error(error); }) fetch(api_uri2, request2) .then(res => res.json()) .then(res => { console.log('update res2', res); this.setState({ rightColumneData: [...this.state.rightColumneData, ...res.results], rightNextKey: res.next, }) }) .catch((error) => { console.error(error); }) } } likeService = async (pk, column) => { let user_info = await AsyncStorage.getItem('user_info'); user_token = JSON.parse(user_info).key; const api_uri = settings.base_uri +'purchase-product/'+ pk +'/like/' var request = { method: 'POST', headers: {'Authorization': 'Token '+ user_token,'Accept': 'application/json','Content-Type': 'application/json', } }; fetch(api_uri, request) .then(res => { if(column=='left'){ var items = this.state.leftColumneData for (i=0; i<items.length; i++) { let item = items[i] if (item.id==pk) { item.is_liked = !item.is_liked items[i] = item; this.setState({ leftColumneData: items }) } } } else { var items = this.state.rightColumneData for (i = 0; i < items.length; i++) { let item = items[i] if (item.id == pk) { item.is_liked = !item.is_liked items[i] = item; this.setState({ rightColumneData: items }) } } } }) .catch((error) => { console.error(error); }); } renderLabel = (text) => { if (text) { return (<View style={{ backgroundColor: 'red', paddingVertical: 2, marginLeft: 3, marginTop: 5, marginBottom: 8, paddingHorizontal: 4 }}><Text style={{ color: 'white', fontSize: 9, fontWeight: '400' }}>{text}</Text></View> ) } else { return (<View style={{ paddingVertical: 2, marginTop: 5, marginBottom: 8, }}></View> ) } } renderList = (item, column) => { return(<View><View style={{ flex: 1, borderColor: 'rgb(136, 136, 136)', borderWidth: 1, marginLeft: 15, marginTop: 15, backgroundColor: 'white', }}><TouchableOpacity onPress={()=>{}}> {item.is_vertical?<Image source={{ uri: item.first_thumbnail }} style={{ flex:1, height: 210 }}/> :<Image source={{ uri: item.first_thumbnail }} style={{ flex: 1, height: 130 }} /> }</TouchableOpacity><View style={{ flex: 1 }}><View style={{ flexDirection: 'row' }}><TouchableOpacity style={{ flex: 4 }}><Text style={{ fontSize: 14, fontWeight: '600', color: 'rgb(35, 31, 32)', marginTop: 10, marginLeft: 8 }}>{item.name}</Text><Text style={{ fontSize: 12, fontWeight: '500', color: 'rgb(35, 31, 32)', marginLeft: 8, marginTop: 8 }}>{item.partner.biz_name}</Text><View style={{ flexDirection: 'row', marginLeft: 6 }}> {this.renderLabel(item.product_type_str)} {this.renderLabel(item.city.name)}</View></TouchableOpacity><View style={{ flex: 1, marginTop: 10, marginRight: 2, alignItems: 'center' }}> {this.props.isLoggedIn ? <View> {item.is_liked ?<Icon name="heart" size={17} color="red" onPress={() => { this.likeService(item.id, column); }} /> : <Icon name="heart-o" size={17} color="red" onPress={() => { this.likeService(item.id, column); }} /> }</View> : <View><Icon name="heart-o" size={17} color="red" onPress={() => { console.log('press heart', column); }} /></View>}</View></View><View style={[ colorStyles.greenBackground, { flex: 1, borderTopWidth: 1, borderColor: 'rgb(136, 136, 136)', padding: 5, paddingRight: 10, }]}><Text style={{ textAlign: 'right', fontSize: 14, fontWeight: '500', color: 'rgb(35, 31, 32)', }}> {(item.price).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}원 {item.id}</Text></View></View></View></View> ) } render() { return (<View style={{ flex: 1 }}><Menu hideModal={this.hideModal.bind(this)} isVisible={this.state.isModalVisible} navigation={this.props.navigation} /><View style={{ flexDirection: 'row' }}><FlatList style={{ flex: 1 }} showsVerticalScrollIndicator={false} initialNumToRender={20} onEndReachedThreshold={1} onEndReached={this.onEndReachedService} refreshing={this.state.refreshing} onRefresh={this.purchaseListService.bind(this)} data={this.state.leftColumneData} renderItem={({ item }) => this.renderList(item, column='left')} keyExtractor={(item) => item.id} /><FlatList style={{ flex: 1, paddingRight: 15 }} showsVerticalScrollIndicator={false} refreshing={this.state.refreshing} onRefresh={this.purchaseListService.bind(this)} data={this.state.rightColumneData} renderItem={({ item }) => this.renderList(item, column='right')} keyExtractor={(item) => item.id} /></View></View> ) }}const mapStateToProps = state => ({ isLoggedIn: state.loginReducer.isLoggedIn})const PurchaseList = connect(mapStateToProps, null)(PurchaseListScreen);export default PurchaseList;