簡體   English   中英

React Native Flatlist 非常慢

[英]React Native Flatlist extremely slow

我正在使用 react native 的 Flatlist,但它非常慢,而且我的項目列表很小(最多大約 20 個項目)。 我使用的是 Native Base,問題是當我想設置一個“selected item”樣式時,大約需要 3 到 4 秒才能觸發

我已經閱讀了大約 10 篇關於如何優化我的 Flatlist 的文章,並且我已經實現了一些優化,比如限制渲染批處理等,但絕對不夠,當我單擊一個項目來改變它的樣式時,它一直在渲染時間長,我覺得小單子上有這么大的延遲是不正常的。 我也試過用另一個類渲染,但還是一樣,即使使用 PureComponent

export default class Example extends React.Component {

closeDrawer () {
  this.drawer._root.close()
};

openDrawer () {
this.drawer._root.open()
};

constructor(props) {
       super(props);
       global.home_signals_url = "https://teosapp-testing.azurewebsites.net/api/ForexSignal/GetHome";
       global.history_signals_url = "https://teosapp-testing.azurewebsites.net/api/ForexSignal/GetHistory";
       this.state = {
             dataSourceHomeSignals: [],
             dataSourceHistorySignals: [],
             isFetching: false,
             showToast: false,
             Textdata:[],
             isReady: false,
             notification: {},
             selectedSignal: this.props.navigation.state.params.id
       };   
       this.isActiveSignal = this.isActiveSignal.bind(this);
       this.selectItem = this.selectItem.bind(this);
}

componentWillMount = async() => {
  await Font.loadAsync({
    Roboto: require('native-base/Fonts/Roboto.ttf'),
    Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf')
  })
  this.setState({ isReady: true })
}

getHomeSignals() {
  this.setState({ isFetching: true }, function() { 
    const url = global.home_signals_url;
     fetch(url, { 
      method: 'GET', 
      headers: new Headers({
        'Authorization': '*****', 
        'Content-Type': 'application/x-www-form-urlencoded'
      })
    }).then((response)=>response.json())
               .then((responseJson)=> {
                 this.setState({
                   dataSourceHomeSignals : responseJson.Signals,
                   isFetching: false
                 })
                })
                .catch((error)=> {
                  console.log(error);
                }) 
   });
}

getHistorySignals() {
  this.setState({ isFetching: true }, function() { 
    const url = global.home_signals_url;
     fetch(url, { 
      method: 'GET', 
      headers: new Headers({
        'Authorization': '*****', 
        'Content-Type': 'application/x-www-form-urlencoded'
      })
    }).then((response)=>response.json())
               .then((responseJson)=> {
                 this.setState({
                   dataSourceHistorySignals : responseJson.Signals,
                   isFetching: false
                 })                     
                })
                .catch((error)=> {
                  console.log(error);
                }) 
   });
}

onRefreshHome() {
  this.getHomeSignals();
}

onRefreshHistory() {
  this.getHistorySignals();
}

signalStatusBG(status) {
  switch(status) {
    case 'Programada':
      return styles.orangeBG;
    case 'Activa':
      return styles.blueBG;
    case 'Ganada':
      return styles.greenBG;
    case 'Cancelada':
      return styles.grayBG;
    default:
      return styles.orangeBG;
  }
}

signalStatusBorder(status) {
  switch(status) {
    case 'Programada':
      return styles.orangeBorder;
    case 'Activa':
      return styles.blueBorder;
    case 'Ganada':
      return styles.greenBorder;
    case 'Cancelada':
      return styles.grayBorder;
    default:
      return styles.orangeBorder;
  }
}

isActiveSignal(index) {
  if(index == this.state.selectedSignal){
    return styles.selectedSignalStyle;
  }
}

isCardSelected(index) {
  if(index == this.state.selectedSignal){
    return styles.chosenItem;
  }
}

selectItem(index) {
  this.setState({selectedSignal: index})
}

_renderItem = ({item, index}) => (
  <MyListItem
       item={item}
       index={index}
       isActiveSignal={this.isActiveSignal}
       selectItem={this.selectItem}
       selectedSignal={this.state.selectedSignal}
  />
);

_keyExtractor = (item, index) => index.toString();

renderItem = ({item, index})=> {
  if (index == 100) {
    return null;
  }

  return (

    <TouchableOpacity          
      onPress={() => this.setState({selectedSignal: index})}>
      <LinearGradient
      colors={['#008696', '#006396', '#192f6a']}
      start={[0,1]}
      end={[1,0]}
      style={[{margin: 10, borderRadius: 5, marginBottom: 10, marginRight: 15, marginLeft: 15, marginTop: 15}, this.isActiveSignal(index), this.signalStatusBorder(item.StatusDisplayName)]}
      >
        <CardItem style={[{backgroundColor: 'rgba(255, 255, 255, 0.0)', paddingLeft: 0, paddingRight:0}, this.isCardSelected(index)]}>
          <Grid>
            <Col style={{flex: 0.35, paddingRight: 15, borderRightWidth: 0.5, borderColor: '#fff'}}>
              <Item style={{borderBottomWidth: 0}}>
                <Image
                  source={require('./assets/flag.png')}
                  style={styles.leftFlag}
                />
                <Image
                  source={require('./assets/flag-en.png')}
                  style={styles.rightFlag}
                />
                <Text style={styles.currencyText}>{item.DisplayName}</Text>
              </Item>

              <Item style={{borderBottomWidth: 0}}>
                <Item style={{borderBottomWidth: 0, flexDirection: 'row', textAlign: 'center'}}>
                <Image
                  source={require('./assets/arrow-down.png')}
                  style={styles.arrow}
                />
                <Text style={styles.typeText}>{item.TypeDisplayName}</Text>
                </Item>                    
              </Item>
              <Item style={{borderBottomWidth: 0, textAlign: 'left', justifyContent: 'flex-start', flexDirection: 'column'}}>                    
                <Text style={styles.bgText}>{item.ExecutionTypeDisplayName}</Text>
                <Text style={styles.spacedText}>Duración: Day</Text>
                <Text style={styles.spacedText}>Caducidad: <Text style={styles.smallData}>{item.ExpirationDate}</Text></Text>
              </Item>
            </Col>
            <Col style={{flex: 0.65, paddingLeft: 5, paddingRight: 5}}>
              <Item style={{borderBottomWidth: 0}}>
                <Text style={[styles.statusText, this.signalStatusBG(item.StatusDisplayName)]}>{item.StatusDisplayName}</Text>
                <Text style={styles.dateText}>{item.ModifiedDate}</Text>
              </Item>                  

              <Item style={{borderBottomWidth: 0}}>
              <Grid>
                <Col style={{flex: 0.5, paddingLeft: 1, paddingRight: 1, flexDirection: 'column'}}>
                <Item style={{borderBottomWidth: 0, alignItems:'flex-start', marginTop: 12}}>                    
                <Item style={{borderBottomWidth: 0, flexDirection: 'column', alignItems:'flex-start'}}>
                <Button
                  style={{
                    paddingTop: 0, 
                    backgroundColor: 'rgba(255, 255, 255, 0.0)', 
                    flexDirection: 'column',
                    alignItems:'flex-start', 
                    textAlign: 'left', 
                    marginBottom: 1,
                    marginTop:0,
                    elevation:0,
                    paddingLeft: 0,

                  }}
                  onPress={() => {
                  let { Textdata } = this.state;
                  Textdata[index] = item.Entry;
                  this.copyValue(JSON.stringify(this.state.Textdata[index]))
                }}>
                  <Text style={styles.valueBoxLabel}><Icon style={styles.miniIcon} active name='log-in' />  Entrada</Text>
                  <Text style={styles.valueBox}>{item.Entry}   <Icon style={styles.miniIcon} active name='copy' /></Text>                      
                  </Button> 
                  <Text style={styles.smallData}>Texto auxiliar</Text>
                </Item>                                     
                </Item>

              <Item style={{borderBottomWidth: 0, alignItems:'flex-start'}}>
                <Item style={{borderBottomWidth: 0, flexDirection: 'column', alignItems:'flex-start'}}>
                <Button
                  style={{
                    paddingTop: 0, 
                    backgroundColor: 'rgba(255, 255, 255, 0.0)', 
                    flexDirection: 'column',
                    alignItems:'flex-start', 
                    textAlign: 'left', 
                    marginBottom: 1,
                    marginTop:0,
                    elevation:0,
                  }}
                  onPress={() => {
                  let { Textdata } = this.state;
                  Textdata[index] = item.StopLoss;
                  this.copyValue(JSON.stringify(this.state.Textdata[index]))
                }}>
                  <Text style={[styles.valueBoxLabel]}><Icon style={styles.miniIcon} active name='remove-circle-outline' />  Salida</Text>
                  <Text style={styles.valueBox}>{item.StopLoss}   <Icon style={styles.miniIcon} active name='copy' /></Text>
                  </Button>
                  <Text style={styles.smallData}>Texto auxiliar</Text>
                </Item>                    
              </Item>
                </Col>
                <Col style={{flex: 0.5, paddingLeft: 1, paddingRight: 1}}>
                <Item style={{borderBottomWidth: 0, alignItems:'flex-start'}}>
                <Item style={{borderBottomWidth: 0, flexDirection: 'column', alignItems:'flex-start'}}>
                <Button
                  style={{
                    paddingTop: 0, 
                    backgroundColor: 'rgba(255, 255, 255, 0.0)', 
                    flexDirection: 'column',
                    alignItems:'flex-start', 
                    textAlign: 'left', 
                    marginBottom: 1,
                    marginTop:0,
                    elevation:0,
                  }}
                  onPress={() => {
                  let { Textdata } = this.state;
                  Textdata[index] = item.TakeProfitOne;
                  this.copyValue(JSON.stringify(this.state.Textdata[index]))
                }}>
                  <Text style={styles.valueBoxLabel}><Icon style={styles.miniIcon} active name='checkmark-circle-outline' />  TP-1</Text>
                  <Text style={styles.valueBox}>{item.TakeProfitOne}   <Icon style={styles.miniIcon} active name='copy' /></Text>
                  </Button>
                </Item>                  
                </Item>

                  <Item style={{borderBottomWidth: 0, alignItems:'flex-start'}}>
                    <Item style={{borderBottomWidth: 0, flexDirection: 'column', alignItems:'flex-start'}}>
                    <Button
                  style={{
                    paddingTop: 0, 
                    backgroundColor: 'rgba(255, 255, 255, 0.0)', 
                    flexDirection: 'column',
                    alignItems:'flex-start', 
                    textAlign: 'left', 
                    marginBottom: 1,
                    marginTop:0,
                    elevation:0,
                  }}
                  onPress={() => {
                  let { Textdata } = this.state;
                  Textdata[index] = item.TakeProfitTwo;
                  this.copyValue(JSON.stringify(this.state.Textdata[index]))
                }}>
                      <Text style={styles.valueBoxLabel}><Icon style={styles.miniIcon} active name='checkmark-circle-outline' />  TP-2</Text>
                      <Text style={styles.valueBox}>{item.TakeProfitTwo}   <Icon style={styles.miniIcon} active name='copy' /></Text>
                      </Button>
                    </Item>                    
                  </Item>

                  <Item style={{borderBottomWidth: 0, alignItems:'flex-start'}}>
                    <Item style={{borderBottomWidth: 0, flexDirection: 'column', alignItems:'flex-start'}}>
                    <Button
                  style={{
                    paddingTop: 0, 
                    backgroundColor: 'rgba(255, 255, 255, 0.0)', 
                    flexDirection: 'column',
                    alignItems:'flex-start', 
                    textAlign: 'left', 
                    marginBottom: 1,
                    marginTop:0,
                    elevation:0,
                  }}
                  onPress={() => {
                  let { Textdata } = this.state;
                  Textdata[index] = item.TakeProfitThree;
                  this.copyValue(JSON.stringify(this.state.Textdata[index]))
                }}>
                      <Text style={styles.valueBoxLabel}><Icon style={styles.miniIcon} active name='checkmark-circle-outline' />  TP-3</Text>
                      <Text style={styles.valueBox}>{item.TakeProfitThree}   <Icon style={styles.miniIcon} active name='copy' /></Text>
                      </Button>
                    </Item>                    
                  </Item>
                </Col>
              </Grid>
              </Item>

              <Item style={{borderBottomWidth: 0, marginTop: 10, justifyContent: 'center'}}>
                <Button
                style={{
                  paddingTop: 3,
                  paddingBottom: 3,
                  height: 28,
                  alignSelf: 'center',
                  textAlign: 'center',
                  backgroundColor: '#e05e55'
                }}
                >
                  <Text>Cerrar</Text>
                </Button>
              </Item>

            </Col>
          </Grid>
        </CardItem>
        <LinearGradient
      colors={['#006396', '#192f6a']}
      start={[0,1]}
      end={[1,0]}
      >
        <CardItem style={{backgroundColor: 'rgba(255, 255, 255, 0.0)', paddingLeft: 0, paddingRight:0, paddingTop:0,paddingBottom:0}}>
          <Accordion
          dataArray={[
            { title: <Text style={{color: '#fff'}}>Ver historial de movimientos</Text>,
            content: 
            item.History.map((item, key) => {
              return (
                <Text key={key} style={{backgroundColor: 'rgba(255, 255, 255, 0.0)' ,paddingTop: 0, color: '#fff', borderBottomWidth: 1, borderBottomColor: 'rgba(255, 255, 255, 0.1)'}}>
                  {item.ModifiedDate}:{"\n"}{item.Message}{"\n"}{"\n"}
                </Text>
              );
            })
          }
          ]}
          headerStyle={{backgroundColor: "rgba(255, 255, 255, 0.0)", borderBottomWidth: 1, borderBottomColor: 'rgba(255, 255, 255, 0.1)'}}
          contentStyle={{paddingTop: 10, backgroundColor: "rgba(255, 255, 255, 0.0)", color: 'rgba(255, 255, 255, 0.7)', paddingLeft: 15, paddingRight: 15}}
          style={{backgroundColor:'rgba(255, 255, 255, 0.0)', width: '100%'}}
          iconStyle={{ color: "white" }}
          icon="arrow-down"
          expandedIcon="arrow-up"
          iconStyle={{ color: "#fff" }}
          expandedIconStyle={{ color: "#fff" }}
          />              
        </CardItem>
        </LinearGradient>
      </LinearGradient>
      </TouchableOpacity>
    );

}

componentDidMount() {
    this.getHomeSignals(); 
    this.getHistorySignals();
}

static navigationOptions = {
  title: 'Welcome',
};

comingSoon() {
  Toast.show({
    text: 'Aún no está disponible esta sección',
    buttonText: "Okay",
    duration: 3000,
  });
}

render() {
  const {navigate} = this.props.navigation;

  if (!this.state.isReady) {
    return <ActivityIndicator />
  }
   return (        
    <Root>
    <Container style={{backgroundColor: '#012435'}}>
    <LinearGradient
      colors={['#002d44', '#002d44', '#002d44']}
      start={[3,0]}
      end={[0,2]}
      >
    <Header 
    hasTabs 
    style={styles.header}
    iosBarStyle="light-content"
    androidStatusBarColor="#2c3e50"
    >
      <Left>
        <Button
            transparent
            onPress={() => navigate('Home')}
        >
          <Icon name='arrow-back' />  
        </Button>
      </Left>
      <Body>
        <Title style={styles.whiteText}>TEOS</Title>
      </Body>
      <Right>
      </Right>
    </Header>
    </LinearGradient>
    <Tabs
    tabContainerStyle={{ borderBottomWidth: 2, borderBottomColor: '#006396' }}
    style={Platform.OS === 'android' ? { overflow: 'hidden' } : null}>
      <Tab heading={ <TabHeading style={{backgroundColor: '#002d44'}}><Icon name="stats" style={{marginRight: 10, color: '#fff'}} /><Text style={Platform.OS === 'android' ? {color: '#fff'} : {color: '#fff'}}>Señales</Text></TabHeading>}>
        <LinearGradient
      colors={['#012435', '#012435']}
      start={[3,0]}
      end={[0,2]}
      style={{flex: 1}}
      >          
      <FlatList 
          data={this.state.dataSourceHomeSignals}
          onRefresh={() => this.onRefreshHome()}
          refreshing={this.state.isFetching}
          renderItem={this.renderItem}
          keyExtractor={this._keyExtractor}
          maxToRenderPerBatch={3}
          windowSize={2}           
          updateCellsBatchingPeriod={3}   
          initialNumToRender={6}
          />
      </LinearGradient>
      </Tab>
      <Tab heading={ <TabHeading style={{backgroundColor: '#002d44'}}><Icon name="paper" style={{marginRight: 10, color: '#fff'}} /><Text style={Platform.OS === 'android' ? {color: '#fff'} : {color: '#fff'}}>Historial</Text></TabHeading>}>
      <LinearGradient
      colors={['#012435', '#012435']}
      start={[3,0]}
      end={[0,2]}
      style={{flex: 1}}
      >          
      <FlatList 
          data={this.state.dataSourceHistorySignals}
          onRefresh={() => this.onRefreshHistory()}
          refreshing={this.state.isFetching}
          renderItem={this.renderItem}
          keyExtractor={this._keyExtractor}
          maxToRenderPerBatch={3}
          windowSize={2}           
          updateCellsBatchingPeriod={3}   
          initialNumToRender={6}
          onEndReachedThreshold={1}
          />
      </LinearGradient>
      </Tab>
    </Tabs>
    <Footer style={{backgroundColor:"rgba(255, 255, 255, 0.0)", borderTopWidth: 2, borderTopColor: '#006396'}}>
      <FooterTab style={{backgroundColor:"#012435"}}>
        <Button 
          onPress={() => navigate('Home')}
          vertical>
          <Icon style={{color: '#fff'}} name="home" />
          <Text style={{color: '#fff'}}>Inicio</Text>
        </Button>
        <Button
          style={{backgroundColor: '#006396', borderRadius: 0}}
          vertical>
          <Icon style={{color: '#fff'}} name="trending-up" />
          <Text style={{color: '#fff'}}>Señales</Text>
        </Button>
        <Button 
          onPress={() => {
            this.comingSoon()
          }}
          vertical>
          <Icon style={{color: '#fff'}} name="walk" />
          <Text style={{color: '#fff'}}>Deportes</Text>
        </Button>
        <Button 
          onPress={() => {
            this.comingSoon()
          }}
          vertical>
          <Icon style={{color: '#fff'}} name="flash" />
          <Text style={{color: '#fff'}}>Crypto</Text>
        </Button>
      </FooterTab>
    </Footer>
  </Container>
  </Root>

      );

}
}

我希望該項目能夠順利地更改其 onPress 樣式,而不會出現大的延遲。 但我有 3-4 秒的延遲

您遇到的這個問題是您的FlatList依賴於data道具以外的data - 即您的renderItem也使用this.state.selectedSignal 所以FlatList不知道它需要重新渲染時this.state.selectedSignal變化。 使用extraData [1] 屬性告訴FlatList關於renderItem依賴的額外數據:

<FlatList 
          data={this.state.dataSourceHistorySignals}
          onRefresh={() => this.onRefreshHistory()}
          refreshing={this.state.isFetching}
          renderItem={this.renderItem}
          keyExtractor={this._keyExtractor}
          maxToRenderPerBatch={3}
          windowSize={2}           
          updateCellsBatchingPeriod={3}   
          initialNumToRender={6}
          onEndReachedThreshold={1}
          extraData={this.state.selectedSignal}
          />

[1]https://facebook.github.io/react-native/docs/flatlist#extradata

即使您擁有一小組數據,列表呈現緩慢的真正問題是因為每個項目是如何在數據數組中創建的。 就像它可以創建為一個新對象一樣,它使整個數據數組成為“新”的東西,flatlist 為其重新呈現所有元素。 就像你對一個項目做“選擇”一樣,flatlist 數據數組會發生變化,即使它們相同,flatlist 也會再次渲染所有項目。

解決此問題的方法是使用 reselect ( https://github.com/reduxjs/reselect ),它在數據未更改時重用對象引用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM