Expo React Native SectionList渲染项目不止一次

时间:2019-06-11 18:53:55

标签: react-native redux expo react-native-flatlist react-native-sectionlist

关于同一问题,我发现了许多问题,但似乎都无法解决问题。目前,我正在使用redux来获取对象数组,操纵该数据以匹配SectionList数据要求,并将其馈送到SectionList section属性。在RenderItem中,我使用FlatList并提供道具propsections.data。最后,FLatList中的renderItem是PureComponent。我已经尝试过使用shouldComponentUpdate和其他人建议的针对节列表的其他道具,但似乎无济于事。 SectionList始终将项目渲染3次或更多次。这是我的代码:

// SECTION LIST COMPONENT 
import React, { Component } from 'react';
import {
View,
SectionList,
ActivityIndicator,
Text,
AsyncStorage,
} from 'react-native';
import { connect } from 'react-redux';
import { getReleases } from '#modules/Collection/actions';
import { Header, SectionFlatList } from '#common/';
class UserCollections extends Component {
static navigationOptions = {
 header: null,
};
state = {
 records: [],
 refreshing: false,
 userData: {},
 page: 1,
};

componentDidMount() {
 this.getUserCollection();
}

getUserCollection = async () => {
 const token = await AsyncStorage.getItem('access_token');
 const tokenSecret = await AsyncStorage.getItem('access_token');
 const user = await AsyncStorage.getItem('userMeta');
 const userMeta = JSON.parse(user);
 const { username } = userMeta;
 const { page } = this.state;
 const folder = 0;
 const accessData = {
   token,
   tokenSecret,
 };
 try {
   await this.props.dispatch(
     getReleases(accessData, username, folder, page)
   );
 } catch (error) {
   console.log('ERROR', error);
 }
};

handleRefresh = () => {
 this.setState(
   {
     page: 1,
     seed: this.state.seed + 1,
     refreshing: true,
   },
   () => {
     this.getUserCollection();
     this.setState({ refeshing: false });
   }
 );
};
handleLoadMore = () => {
 this.setState(
   {
     page: this.state.page + 1,
   },
   () => {
     this.getUserCollection();
   }
 );
};
renderFooter = () => {
 if (!this.state.loading) return null;
 return (
   <View
     style={{
       paddingVertical: 20,
       borderTopWidth: 1,
       borderColor: '#CED0CE',
     }}
   >
     <ActivityIndicator animating size="large" />
   </View>
 );
};

_sectionKeyExtractor = (section, index) => 'S' + index.toString();

_renderSections = ({ section }) => (
 <SectionFlatList {...this.props} section={section} />
);

render() {
 const { releases } = this.props;

 return (
   <View style={styles.mainContainer}>
     <View style={styles.headerContainer}>
       <Header headerText={'Collection'} />
     </View>
     <View style={styles.contentContainer}>
       <SectionList
         sections={releases}
         extraData={releases}
         renderItem={this._renderSections}
         renderSectionHeader={({ section: { title } }) => (
           <Text style={{ fontWeight: 'bold', color: '#fff' }}>{title}</Text>
         )}
         keyExtractor={this._sectionKeyExtractor}
         refreshing={this.state.refreshing}
         onRefresh={this.handleRefresh}
         onEndReached={this.handleLoadMore}
       />
     </View>
   </View>
 );
}
}

const styles = {
textContainer: {
 paddingBottom: 50,
},
contentContainer: {
 flex: 1,
 backgroundColor: '#000',
 alignItems: 'center',
 justifyContent: 'space-around',
},
contentContainerStyle: {
 flexDirection: 'column',
},
mainContainer: {
 flex: 1,
},
};

mapStateToProps = state => {
console.log('MAP STATE TO PROPAS STATE', state);
return {
 releases: state.UserCollection.releases,
};
};

export default connect(mapStateToProps)(UserCollections);
// SECTIONLIST RENDERITEM COMPONENT (FLATLIST)
import React, { PureComponent } from 'react';
import { FlatList, View } from 'react-native';
import { isEqual } from 'lodash';
import { RecordItem } from './';

class SectionFlatList extends PureComponent {
  listKey = (item, index) => 'D' + index.toString();

  render() {
    const {
      section,
      navigation,
      screenProps: {
        user: {
          accessData: { userMeta },
        },
      },
    } = this.props;
    return (
      <View style={{ flex: 1 }}>
        <FlatList
          data={section.data}
          renderItem={({ item }) => (
            <RecordItem
              item={item}
              navigation={navigation}
              userMeta={userMeta}
            />
          )}
          keyExtractor={this.listKey}
          ListFooterComponent={this.renderFooter}
          contentContainerStyle={styles.contentContainerStyle}
          numColumns={3}
          maxToRenderPerBatch={11}
          initialNumToRender={11}
          removeClippedSubviews={false}
        />
      </View>
    );
  }
}

const styles = {
  contentContainerStyle: {
    flexDirection: 'column',
  },
};

export { SectionFlatList };
import React, { PureComponent } from 'react';
import { TouchableOpacity, View, Image } from 'react-native';
class RecordItem extends PureComponent {
  // shouldComponentUpdate(nextProps, nextState) {
  //   if (this.props.item.instance_id === nextProps.item.instance_id)
  //     return false;
  // }
  render() {
    const { item } = this.props;
    return (
      <View>
        <TouchableOpacity
          key={item.instance_id}
          onPress={() => {
            navigation.navigate('AlbumDetail', {
              item: item,
              inCollection: true,
              inWantlist: false,
              userData: userMeta,
            });
          }}
        >
          <Image
            style={styles.albumCovers}
            source={{ uri: item.basic_information.cover_image }}
          />
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = {
  albumCovers: {
    height: 124,
    width: 124,
    marginLeft: 0.5,
    marginRight: 0.5,
    marginTop: 0.5,
    marginBottom: 0.5,
  },
};

export { RecordItem };

ROWS RENDERING MORE THAN ONCE

1 个答案:

答案 0 :(得分:0)

我有类似的问题。我没有使用redux,所以我的代码与您需要的代码不完全相同。我认为您有两种不同的原因都可能导致这种情况。

  1. this.setState并不总是立即更改状态,因此在handleLoadMore内部,您可能要进行某种检查以确保状态已完成更新,然后再调用getUserCollection,否则它可能在同一页面上调用两次。 / li>
  2. FlatList(以及react-native中的其他类型的列表)有时会重复调用onEndReached。可以通过在列表中使用bounces = {false}来减少这种情况。即使执行此操作,您可能仍需要创建检查以确保它不会在上次完成更新之前不会尝试再次更新。也许您可以出于您的目的修改我用来解决问题的方式。这是一个示例:

在调用onEndReached时调用这样的内容

_onEndList = () => {

if(this.state.showFooter === false && this.state.stuff.length > 0){
    //console.log('getting end list');
    this.AddMoreStuff();


  }else{
//console.log('waiting end list');
    this.waitingForMoreData();
  }

}

然后,等待waitingForMoreData看起来像这样

 waitingForMoreData(){

   setTimeout(() => {
     if(this.state.showFooter === false && this.state.stuff.length > 0){
       //console.log('getting after waiting end list');

       this.AddMoreStuff();
     }else{
       //console.log('waiting again end list');
       this.waitingForMoreData();
     }
   }, 15000)

 }

AddMoreStuff在执行其他任何操作之前将showFooter设置为true,并在进行其他所有操作后将showFooter设置为false。这些函数正在检查以确保stuff.length> 0,以便在您初次访问页面时不会与列表的初始填充同时被调用。我正在使用其他功能来做到这一点。我使用setTimeout而不是告诉它不要调用AddMoreStuff,因为如果上次调用列表时未更新列表,则FlatList有时会停止调用onEndReached。我希望这对我有帮助,即使我没有使用redux。