应对本机性能问题

时间:2018-08-25 20:06:57

标签: reactjs react-native

我正在使用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] ,我发现我的应用程序内部发生了无休止的渲染,这可能会使我的线程忙碌(我的意思是无休止的&不必要地传递道具)。我在单独的Stackoverflow thread上问了同样的问题,但没有得到适当的答复,并且由于它与绩效有关,因此我想在这里悬赏。

请检查以下线程:infinite Render in React

[答案更新:] 尽管这里有很多不错的答案,以防万一有人想了解它的工作原理,您可以克隆我的存储库并回到之前commit。我已将提交链接到解决我的问题的位置(因此您可能需要回去看看我做错了什么)。另外,所有答案都很有用,而且不难理解,因此您一定要仔细阅读。

5 个答案:

答案 0 :(得分:6)

每次组件更新时,它都会启动一个新的套接字,这会导致内存泄漏,并导致对同一数据多次调用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>}
            />
        );
    }
}

答案 1 :(得分:5)

有很多标准方法可以提高应用程序的响应性能,这是最常见的方法:

  • 使用常规的反应优化(shouldComponentUpdate,PureComponent-阅读文档)
  • 使用虚拟列表(限制数据的可见部分)

在这种情况下,我会添加:

在优化之前不处理数据-f.e.格式化没有改变的数据至少是不必要的。您可以插入中间组件(优化层),仅在发生“原始数据”更改时才将经过格式化的数据传递/更新到<CoinCard />中。

当在一个位置/简单结构中使用数据时,完全

You might not need Redux (将数据存储在状态中)。当然,您可以将redux用于其他共享应用状态(例如过滤选项)。

使用<FlatList />(本机),搜索更适合的某物?

更新

某些代码在同时进行了更改(回购),但此时(08.09)一个问题仍然存在,可能导致内存泄漏。

您要在每次this.socket.on调用中调用componentDidUpdate(错误编码的条件)-不断添加新的处理程序!

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;
        }
      }
    })
  }
}

次要错误:在化简器中,初始获取状态设置为true。

搜索性能问题,我会研究<CoinCard />

  • 将其设为PureComponent;
  • increaseddecreased不需要保存在强制进行不必要的渲染调用的状态; <​​/ li>
  • 我将使用更新时间(未保存在状态中,仅作为prop在父级中传递,并且仅用于上面代码中的updateCoinData中的更新行)并得出差异的方向(仅检查0和符号) 已经在perc中计算得出)仅适用于可见项(来自渲染),仅在时间限制内(渲染时间与数据之间的差异)更新道具)。也可以使用setTimeout
  • 最终删除componentWillReceivePropscomponentDidUpdateshouldComponentUpdate应该(高度?)提高性能;

答案 2 :(得分:2)

在呈现Flatlist时,您应该考虑仅使用PureComponent或使用shouldComponentUpdate钩子进行更新。

来自doc

  

如果您的应用程序呈现了很长的数据列表(数百行或数千行),我们建议使用一种称为“窗口”的技术。该技术在任何给定时间仅呈现一小部分行,并且可以大大减少重新呈现组件所需的时间以及所创建的DOM节点的数量。

深入了解此performance guide

如果您仍然希望进行一些高级浏览,那么我建议您研究以下线程:

FlatList and VirtualizedList Scroll performance is laggy after 30+ rows

Performance problems with react when using a big list

答案 3 :(得分:2)

就像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>
        )
    }
}

答案 4 :(得分:1)

  1. 永远不要在React的componentWillMount()生命周期方法中进行API调用,而应该在componentDidMount()中进行。

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

  

许多人会试图使用此功能,以便发送请求以获取数据并期望在初始渲染准备就绪之前数据就可用。情况并非如此-虽然请求将在渲染之前初始化,但在调用渲染之前将无法完成。

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