[英]React native performance issue
我正在使用coincap api首先獲取大約1500多種加密貨幣的數據,然后通過網絡套接字更新加密貨幣的更新值。
我正在使用Redux在這里管理我的狀態
在我的componentDidMount()
內部,我正在調用redux動作 fetchCoin
來獲取硬幣的價值
componentDidMount() {
this.props.fetchCoin()
}
然后作為return
我正在做這樣的事情
<FlatList
data={this.state.searchCoin ? displaySearchCrypto : this.props.cryptoLoaded}
renderItem={({ item }) => (
<CoinCard
key={item["short"]}
coinShortName = {item["short"]}
coinName = {item["long"]}
coinPrice = {item["price"].toFixed(2)}
percentChange = {item["perc"].toFixed(2)}
/>
然后我有一個網絡套接字,可以像這樣更新加密貨幣的價值
componentDidUpdate() {
if (this.state.updateCoinData || this.updateCoinData.length < 1 ) {
this.updateCoinData = [...this.props.cryptoLoaded];
this.setState({updateCoinData: true})
}
this.socket.on('trades', (tradeMsg) => {
for (let i=0; i< this.updateCoinData.length; i++) {
if (this.updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
this.updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
this.updateCoinData[i]["price"] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(this.updateCoinData);
}
}
})
}
現在,盡管這項工作有效,但問題在於,這使我的應用程序變慢了,因為每當套接字發送新數據時,它都必須渲染每個組件,因此諸如觸摸和搜索之類的事件需要大量時間來執行。 [更新]事實證明,即使我刪除套接字連接,我的應用也正在呈現某些內容, 請查看更新2
[問題:]我應該怎么做才能提高App的性能? (諸如不使用狀態或使用DOM來更新我的應用之類的東西)。
[更新1:]我正在使用https://github.com/irohitb/Crypto而這兩個是所有發生邏輯的js文件https://github.com/irohitb/Crypto/blob/master/src/container /cryptoContainer.js https://github.com/irohitb/Crypto/blob/master/src/components/CoinCard.js我也從地圖轉到了Flatlist。
[更新:2]我發現我的應用程序內部發生了無休止的渲染 ,這可能使我的線程處於繁忙狀態( 我的意思是,它是無休止的並且不必要地傳遞了props )。 我在單獨的Stackoverflow線程上問了同樣的問題,但是沒有得到適當的答復,並且由於它與性能有關,因此我考慮在此處懸賞。
請檢查以下線程: React中的無限渲染
[答案更新:]盡管這里有很多不錯的答案,以防萬一有人想了解它是如何工作的,您可以克隆我的存儲庫並在 提交 之前返回到。 我已將提交鏈接到解決我的問題的位置(因此您可能需要回去看看我做錯了什么)。 另外,所有答案都很有用,而且不難理解,因此您一定要仔細閱讀。
每次組件更新時,它都會啟動一個新的套接字,這會導致內存泄漏並導致this.props.updateCrypto(updateCoinData);
對於同一數據,將被多次調用。 這可以通過在打開插座固定componentDidMount()
和在關閉它componentWillUnmount()
您還可以每隔幾秒鍾一次緩沖多個記錄更新並更改FlatList數據。
編輯工作示例(App.js):
import React, { Component } from 'react';
import { Text, View, FlatList } from 'react-native';
import SocketIOClient from 'socket.io-client';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.currencies = {};
this.state = {
currenciesList: [],
}
}
componentDidMount() {
this.socket = SocketIOClient('https://coincap.io');
this.socket.on('trades', (tradeMsg) => {
const time = new Date();
// Store updates to currencies in an object
this.currencies[tradeMsg.message.msg.short] = {
...tradeMsg.message.msg,
time: time.getHours() + ':' + time.getMinutes() + ':' + time.getSeconds(),
};
// Create a new array from all currencies
this.setState({currenciesList: Object.values(this.currencies)})
});
}
componentWillUnmount() {
this.socket.disconnect();
}
render() {
return (
<FlatList
data={this.state.currenciesList}
extraData={this.state.currenciesList}
keyExtractor={(item) => item.short}
renderItem={({item}) => <View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{flex: 1}}>{item.time}</Text>
<Text style={{flex: 1}}>{item.short}</Text>
<Text style={{flex: 1}}>{item.perc}</Text>
<Text style={{flex: 1}}>{item.price}</Text>
</View>}
/>
);
}
}
有很多標准方法可以提高React應用的性能,最常見的方法是:
在優化之前不要處理數據 -格式化沒有改變的數據至少是不必要的。 您可以插入中間組件(優化層),僅在發生“原始數據”更改時,這些組件才會將格式化的數據傳遞/更新到<CoinCard />
。
當在一個位置/簡單結構中使用數據時, 您可能根本不需要Redux (將數據存儲在狀態中)。 當然,您可以將redux用於其他全局共享的應用程序狀態(fe過濾選項)。
使用<FlatList />
(react-native),搜索更合適的東西嗎?
平均時間 (repo) 更改了一些代碼 ,這時(08.09)一個問題仍然存在,可能導致內存泄漏。
您在每個componentDidUpdate
調用上調用this.socket.on
(錯誤編碼的條件)-不斷添加新的處理程序!
componentDidUpdate() {
// call all ONLY ONCE afer initial data loading
if (!this.state.updateCoinData && !this.props.cryptoLoaded.length) {
this.setState({updateCoinData: true}) // block condition
this.socket.on('trades', (tradeMsg) => {
// slice() is faster, new array instance
// let updateCoinData = [...this.props.cryptoLoaded];
let updateCoinData = this.props.cryptoLoaded.slice();
for (let i=0; i<updateCoinData.length; i++) {
//Search for changed Crypto Value
if (updateCoinData[i]["short"] == tradeMsg.coin ) {
// found, updating from message
updateCoinData[i]["long"] = tradeMsg["message"]["msg"]["long"]
updateCoinData[i]["short"] = tradeMsg["message"]["msg"]["short"]
updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
updateCoinData[i]["mktcap"] = tradeMsg['message']['msg']["mktcap"]
updateCoinData[i]["price"] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(updateCoinData);
// record found and updated, no more looping needed
break;
}
}
})
}
}
輕微錯誤 :Reducer中的初始獲取狀態設置為true。
搜索性能問題,我將查看<CoinCard />
:
increased
和decreased
狀態保存為強制不必要的渲染調用的狀態; updateCoinData
在上面的代碼)並從中獲得方向(檢查0,只簽)的區別( 在已經計算出的perc
) 僅適用於可見項 (來自渲染)並且僅在時間限制內 (渲染時間與數據更新屬性之間的差異)。 setTimeout
也可以使用。 componentWillReceiveProps
, componentDidUpdate
和shouldComponentUpdate
應該(高度?)提高性能; 呈現Flatlist時,您應考慮使用PureComponent
或僅應在需要時利用shouldComponentUpdate
掛鈎進行更新。
從文檔 :
如果您的應用程序呈現了很長的數據列表(數百行或數千行),我們建議使用一種稱為“窗口”的技術。 該技術在任何給定時間僅呈現一小部分行,並且可以大大減少重新呈現組件所花費的時間以及所創建的DOM節點的數量。
深入了解本性能指南 。
如果您仍然希望進行高級瀏覽,那么我建議您研究以下線程:
就像Bhojendra Rauniyar所說的那樣,您應該在CoinCard中使用shouldComponentUpdate。 您可能還想更改FlatList,縮小的樣本在ScrollView中具有FlatList,這將導致FlatList完全展開,從而立即呈現其所有項目。
class cryptoTicker extends PureComponent {
componentDidMount() {
this.socket = openSocket('https://coincap.io');
this.props.fetchCoin()
this.props.CurrencyRate()
this.socket.on('trades', (tradeMsg) => {
for (let i=0; i< this.updateCoinData.length; i++) {
if (this.updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
this.updateCoinData["short"] = tradeMsg["message"]["msg"]["short"]
this.updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
this.updateCoinData[i]["price"] = tradeMsg["message"]['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(this.updateCoinData);
}
}
})
}
componentWillReceiveProps(newProps){
// Fill with redux data once
if (this.updateCoinData.length < 1 && newProps.cryptoLoaded) {
this.updateCoinData = [...newProps.cryptoLoaded];
}
}
render() {
return (
<View style={{height: '100%'}}>
<Header/>
<FlatList
style={{flex:1}}
data={this.props.cryptoLoaded}
keyExtractor={item => item.short}
initialNumToRender={50}
windowSize={21}
removeClippedSubviews={true}
renderItem={({item, index}) => (
<CoinCard
index={index}
{...item}
/>
)}
/>
</View>
)
}
}
class CoinCard extends Component {
shouldComponentUpdate(nextProps) {
return this.props.price !== nextProps.price || this.props.perc !== nextProps.perc
}
render() {
console.log("here: " + this.props.index);
return (
<View>
<Text> {this.props.index} = {this.props.long} </Text>
</View>
)
}
}
您永遠不要在React的componentWillMount()生命周期方法中進行API調用,而應該在componentDidMount()中進行。
請查看有關生命周期方法以及如何使用哪種方法的完整文章: https : //medium.com/@baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d 。
許多人會試圖使用此功能來發送請求以獲取數據,並期望在初始渲染准備就緒之前數據就可用。 情況並非如此-盡管請求將在渲染之前初始化,但在調用渲染之前它將無法完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.