React Native Flatlist extremely slow



我正在使用 react native 的 Flatlist,但它非常慢,而且我的项目列表很小(最多大约 20 个项目(。我正在使用Native Base,问题是当我想设置"选定项目"样式时,大约需要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>
);
}
}

我希望该项目在Press上顺利地改变其样式,而不会有太大的延迟。但是我有 3-4 秒的滞后

您遇到的这个问题是您的FlatList依赖于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 会重新渲染所有元素。就像您对项目进行"选择"时一样,平面列表数据数组会发生变化,使平面列表再次呈现所有项目,即使它们相同。

解决此问题的方法是使用 reselect (https://github.com/reduxjs/reselect(,当数据未更改时,它会重用对象引用。

最新更新