4

I have been working my way through legacy views within an app - resolving the issues of FlatLists within a ScrollView component causing the resulting Virtualised Lists error that is displayed.

I have 5 affected pages - first 3 only had 1 flatlist in the view - so was easy enough to split the surrounding code into flatlist header and footer assets. However I'm not sure what to do in terms of having 2 or more flatlists - how do i approach the layout in this scenario - so there is only 1 scroll?

I may be missing something very simple but need a nudge, please!

here is the view code:

<View style={[PRStyles.IRContainer]} >
        <StatusBar barStyle="light-content" />
        <View style={PRStyles.header}>
          <FixedHeader backButton={true} navScreen='HomeViewContainer' />
        </View>
        
        <View style={PRStyles.IRBody}>
          <ScrollView
            refreshControl={
              <RefreshControl
                refreshing={this.state.refreshing}
                onRefresh={this._onRefresh} />}>
            <KeyboardAvoidingView> 
            <TitleHeader sectionLocaleTxt='Duty Record' sectionTxt='' sectionDesc='End of shift duty Record.' sectionHyphen={false}  />
            <View style={FormStyles.PrRow}>
              <Text style={FormStyles.PrRowTitle}>{this.props.auth.checkedInVenueName}</Text>
              <Text style={FormStyles.PrRowDate}>{this.getCurrentDate()}</Text>
            </View>
            <View style={FormStyles.PrRow}>
              <Text style={FormStyles.PrRowSubTitle}>General Manager / Licence Holder:</Text>
              <View style={FormStyles.PrTable}>
                <View style={FormStyles.prRowStrip}><Text style={FormStyles.prRowStripText} >{this.state.licenceHolder}</Text></View>
              </View>
            </View>

            <View style={FormStyles.PrRow}>
              <Text style={FormStyles.PrRowSubTitle}>Door Staff (<Text style={FormStyles.PrRowCount}>{this.state.doorStaffCount}</Text> total)</Text>
              <View style={FormStyles.PrTable}>
                <FlatList
                  scrollEnabled={true}
                  data={this.state.rotaRecords}
                  keyExtractor={(item, index) => index.toString()}
                  ListEmptyComponent={this._listStaffEmptyComponent}
                  renderItem={this._renderDoorStaffItem}
                />
              </View>

            </View>
            <View style={FormStyles.PrRow}>
              <Text style={FormStyles.PrRowSubTitle}>Numbers:</Text>
              <View style={FormStyles.PrTable}>
                <View style={FormStyles.prRowStrip}><Text style={FormStyles.prRowStripText} >Total In <Text style={ FormStyles.prRowStripColon}>:</Text> <Text style={FormStyles.prRowStripOrText}>{this.state.totalIn}</Text></Text></View>
                <View style={FormStyles.prRowStrip}><Text style={FormStyles.prRowStripText} >Total Out<Text style={FormStyles.prRowStripColon}>:</Text> <Text style={FormStyles.prRowStripOrText}>{this.state.totalOut}</Text></Text></View>
                <View style={FormStyles.prRowStrip}><Text style={FormStyles.prRowStripText} >Overall Difference<Text style={FormStyles.prRowStripColon}>:</Text> <Text style={FormStyles.prRowStripOrText}>{this.state.totalDifference}</Text></Text></View>
              </View>

            </View>

            <View style={FormStyles.PrRow}>
              <Text style={FormStyles.PrRowSubTitle}>Door Counts:</Text>
              <FlatList
                  scrollEnabled={true}
                  data={this.state.countRecords}
                  keyExtractor={(item, index) => index.toString()}
                  ListEmptyComponent={this._listDoorCountEmptyComponent}
                  ListHeaderComponent={this._listDoorCountHeaderComponent}
                  renderItem={this._renderDoorCountItem}
                />
          </View>

            <View style={[FormStyles.form, FormStyles.PrRow, {marginTop:15, paddingTop:0, borderBottomWidth:0} ]}>
              <Text style={ModalStyles.formTop}><Text style={[ModalStyles.required, ]}>*</Text>Required Field</Text>
              <Text style={[FormStyles.formLabel, FormStyles.formlabelFirst ]}>1. Customer Comments:</Text>

              <View style={FormStyles.textInputBlock}>
                <TextInput
                  placeholder="Enter Comments"
                  numberOfLines={4}
                  onChangeText={val => this.setState({ comments: val})}
                  value={this.state.comments}
                  multiline
                  style={{minHeight: 280, height: 'auto', textAlignVertical: 'top'}}
                />
              </View>

              <Text style={[FormStyles.formLabel, FormStyles.formlabelFirst ]}>2. Duty Manager Name<Text style={ModalStyles.required}>*</Text> :</Text>

              <View style={FormStyles.textInputBlock}>
                <TextInput
                  ref='signatureName'
                  placeholder="Please Print Name"
                  style={FormStyles.textInputText}
                  autoCorrect={false}
                  returnKeyType='done'
                  value={this.state.signatureName}
                  onChangeText={(text) => this.setState({signatureName:text})}
                />
              </View>
              <Text style={[FormStyles.formLabel, FormStyles.formlabelFirst ]}>3. Duty Manager Signature: <Text style={ModalStyles.required}>*</Text></Text>
                  <Text style={[FormStyles.formLabelSub, FormStyles.formLabelSubHigh, FormStyles.superHighLight ]}>Note: PRESS BLUE SAVE BUTTON after applying Signature</Text>
              <View style={[FormStyles.textInputBlock, this.isSignatureAdded() && FormStyles.signatureBlock ]}>
                {this.signatureBlock()}

              </View>
            </View>

            {submitButton}
           </KeyboardAvoidingView>
          </ScrollView>
        </View>
      </View>
Sadeed_pv
  • 513
  • 1
  • 9
  • 22
Dancer
  • 17,035
  • 38
  • 129
  • 206
  • 2
    Well, the usage of FlatList is due performance optimisations while scrolling and especially for large list scrolling. Nesting them inside another ScrollView should break those optimisations, so you should be free to just map over your arrays inside a scroll view if i understand everything correctly. Maybe i don't understand the motivation of nesting Flatlists inside Scrollview since i've never seen that before. – Mod3rnx Feb 11 '22 at 13:47

2 Answers2

1

This is the most common error when working with scroll view and flat list.

To prevent the error cause, we have to manage our views inside a single flat list and put other components in the list header component and list footer component in an efficient way as we desire.

<FlatList
    data={this.state.countRecords}
    renderItem={(item) => {
        return (
            // Your flat list item goes here..
        )
    }}
    ListHeaderComponent={
        // Content above the list goes here..
    }
    ListFooterComponent={
        // Content below the list should goes here..
    }
/>

You can still check the below link for more understanding.

FlatList Example with Custom Header and Custom Footer

Vicky Ahuja
  • 906
  • 3
  • 9
  • Thanks @Vicky - but this tells me what I already know in terms of utilising the flatlists header and footer components to remove the scrollview and the error If I had 1 FLATLIST in my view - but my question was how to work around the problem of having 2 or more flatlists in one view - allowing the whole page to scroll - whether this is even possible to resolve. – Dancer Feb 11 '22 at 12:30
  • 1
    @Dancer, Till now I know the only solution is only with this method. – Vicky Ahuja Feb 12 '22 at 05:11
  • 1
    Thanks @Vicky - seems i'll have to re-design the views if this is the case. thanks for your help – Dancer Feb 14 '22 at 15:36
0

In my case, I had two different flatlist (A and B) in a parent Scrollview component and I could not re-design the views cause I wanted to keep the same user experience.

This is how I handled this painfull issue.

  1. I created a Flatlist as a parent component instead of the ScrollView
  2. I used the prop 'ListHeaderComponent' to integrate my two Flatlist in the parent Flatlist (if you have more than 2 Flatlist it is the same)
  3. I used the prop 'ListFooterComponent' to integrate the other components below the two Flatlist
  4. As the value of the prop 'data' of the parent Flatlist, I used 'null'
  5. As the value of the prop 'renderItem' of the parent Flatlist, I used 'null'.
  6. I also used two local states (displayFlatList_A and displayFlatList_B) to render FlatListA OR FlatListB not in the same time (avoiding the VirtualizedList console error)

This is my code related to the render :

<FlatList 
ListHeaderComponent={
    <View style={styles.container}>
        <Header title="My header title..."/>
        <Text style={styles.text}>Searchbar label...</Text>
        <SearchBar 
           value={searchContent}
           onChange={handleSearchActivities}
           placeholder="My placeholder A" 
        /> 
        {displayFlatList_A &&
         <FlatList_A
           ItemSeparatorComponent={() => (<View style={styles.separatorItem} />)}
           data={dataFlatListA}
           renderItem={renderItemFlatListA}
           keyExtractor={(item) => `${item.id}`}
         />
        }
        <Text style={styles.label}>Searchbar label...</Text>
        <SearchBar 
            value={selectSchoolName}
            onChange={handleChange}
            placeholder="My placeholder B" 
        />
        {displayFlatList_B &&
         <FlatList_B
           ItemSeparatorComponent={() => (<View style={styles.separatorItem} />)}
           data={dataFlatListB}
           renderItem={renderItemFlatListB}
           keyExtractor={(item) => `${item.id}`}
         />
        }
    </View>  
}
ListFooterComponent={
    <ButtonComponent
        label='Button Label'
        action={myAction}
    />
}
data={null}
renderItem={null}

/>

I hope it will help some of you.

jbb
  • 56
  • 4