簡體   English   中英

反應本機性能問題

[英]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應用的性能,最常見的方法是:

  • 使用常規的反應優化(shouldComponentUpdate,PureComponent-閱讀文檔)
  • 使用虛擬列表(限制數據的可見部分)

在這種情況下,我將添加:

在優化之前不要處理數據 -格式化沒有改變的數據至少是不必要的。 您可以插入中間組件(優化層),僅在發生“原始數據”更改時,這些組件才會將格式化的數據傳遞/更新到<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 />

  • 使它成為PureComponent;
  • 不需要將increaseddecreased狀態保存為強制不必要的渲染調用的狀態;
  • 我會用更新時間(不保存狀態,因為在父母道具剛過,只有更新的行內updateCoinData在上面的代碼)並從中獲得方向(檢查0,只簽)的區別( 在已經計算出perc僅適用於可見項 (來自渲染)並且僅在時間限制內 (渲染時間與數據更新屬性之間的差異)。 setTimeout也可以使用。
  • 最后刪除componentWillReceivePropscomponentDidUpdateshouldComponentUpdate應該(高度?)提高性能;

呈現Flatlist時,您應考慮使用PureComponent或僅應在需要時利用shouldComponentUpdate掛鈎進行更新。

文檔

如果您的應用程序呈現了很長的數據列表(數百行或數千行),我們建議使用一種稱為“窗口”的技術。 該技術在任何給定時間僅呈現一小部分行,並且可以大大減少重新呈現組件所花費的時間以及所創建的DOM節點的數量。

深入了解本性能指南

如果您仍然希望進行高級瀏覽,那么我建議您研究以下線程:

30列以上后,FlatList和VirtualizedList滾動性能不佳

使用大清單時反應的性能問題

就像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>
        )
    }
}
  1. 您永遠不要在React的componentWillMount()生命周期方法中進行API調用,而應該在componentDidMount()中進行。

    請查看有關生命周期方法以及如何使用哪種方法的完整文章: https : //medium.com/@baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d

許多人會試圖使用此功能來發送請求以獲取數據,並期望在初始渲染准備就緒之前數據就可用。 情況並非如此-盡管請求將在渲染之前初始化,但在調用渲染之前它將無法完成。

  1. 您可能要使用redux subscibe / unsubscribe方法,而不是創建用於更新coinData的套接字。

暫無
暫無

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

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