![](/img/trans.png)
[英]React Native FlatList makes app extremely slow after 10 elements
[英]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.