【问题标题】:How can I improve the performance of a recursive component in React?如何提高 React 中递归组件的性能?
【发布时间】:2019-04-19 17:18:46
【问题描述】:

在我的 React Native Expo 项目中,我创建了一个调用自身的递归注释组件,以呈现嵌套的注释线程。

但是,一旦 cmets 的数量太大,性能就会开始下降,因为它对用户不是很友好。我尝试改变 api 调用的结构以检索该 cmets 和一些其他小的调整,但无济于事.

我认为我需要以某种方式使用 shouldComponentUpdate 仅在某些状态发生变化时重新渲染,主要是评论是否打开或折叠。

有没有办法提高 React 中递归组件的性能?

import { FontAwesome } from "@expo/vector-icons";
import dateFns from "date-fns";
import { inject } from "mobx-react";
import PropTypes from "prop-types";
import React from "react";
import {
  ActivityIndicator,
  StyleSheet,
  TouchableHighlight,
  View
} from "react-native";
import Collapsible from "react-native-collapsible";
import HTMLView from "react-native-htmlview";
import { Text, withTheme } from "react-native-paper";
import ApiService from "../services/ApiService";

class Comment extends React.Component {
  static propTypes = {
    api: PropTypes.instanceOf(ApiService).isRequired,
    commentIds: PropTypes.arrayOf(PropTypes.number).isRequired
  };

  state = {
    comments: null
  };

  componentDidMount() {
    this.fetchStoryComments();
  }

  async fetchStoryComments() {
    const { api, commentIds } = this.props;
    try {
      let comments = await api.fetchStoryComments(commentIds);
      comments = comments.map(comment => {
        const commentCopy = comment;
        commentCopy.isCollapsed = false;
        return commentCopy;
      });
      this.setState({
        comments
      });
    } catch (error) {
      // HANDLE ERROR
    }
  }

  toggleCollapse(commentId) {
    this.setState(prevState => ({
      comments: prevState.comments.map(comment =>
        comment.id === commentId
          ? Object.assign(comment, { isCollapsed: !comment.isCollapsed })
          : comment
      )
    }));
  }

  render() {
    const { comments } = this.state;
    const { api } = this.props;

    return comments ? (
      comments.map(
        comment =>
          comment && (
            <View key={comment.id} style={styles.commentContainer}>
              <TouchableHighlight
                onPress={() => this.toggleCollapse(comment.id)}
              >
                <View style={styles.commentHeader}>
                  <Text style={styles.commentHeaderText}>
                    {`${comment.by} ${dateFns.distanceInWordsToNow(
                      new Date(comment.time * 1000)
                    )} ago`}
                  </Text>
                  <Text style={styles.commentHeaderText}>
                    {comment.isCollapsed ? (
                      <FontAwesome name="plus" />
                    ) : (
                      <FontAwesome name="minus" />
                    )}
                  </Text>
                </View>
              </TouchableHighlight>
              <Collapsible duration={200} collapsed={comment.isCollapsed}>
                <View style={styles.comment}>
                  <HTMLView
                    addLineBreaks={false}
                    stylesheet={htmlStyles}
                    textComponentProps={{ style: styles.commentText }}
                    value={comment.text}
                  />
                  {"kids" in comment && (
                    <View style={styles.kids}>
                      <Comment api={api} commentIds={comment.kids} />
                    </View>
                  )}
                </View>
              </Collapsible>
            </View>
          )
      )
    ) : (
      <ActivityIndicator
        style={styles.activityIndicator}
        size="small"
        color="#fff"
      />
    );
  }
}

export default inject("api")(withTheme(Comment));

【问题讨论】:

  • 限制递归迭代的数量并将其余部分隐藏在“查看更多”后面
  • 是的,我已经考虑过了。临时修复可能不是一个坏主意,但从长远来看,用户体验不会那么好..
  • 为什么不呢?大多数大型网站都以某种方式合并了这一点 - facebook、twitter,甚至 stackoverflow
  • 确实如此。我肯定会保留它作为一个选项。我更喜欢类似于 FlatList 组件的无限滚动。
  • 这是一篇关于为什么无限滚动可能是一个坏主意的好文章:logrocket.com/blog/infinite-scroll

标签: javascript reactjs react-native


【解决方案1】:

您是否尝试过使用flatlist 而不是映射一堆视图?

这里有一个guide 解释如何使用它。

编辑 1:

您实际上可以将sectionlistflatlist 用于您正在构建的复杂结构。

这是一个小例子:

render() {
    const a = ['comment 1 a', 'comment 2 a', 'comment 3 a'];
    const b = ['comment 1 b', 'comment 2 b'];
    const c = ['comment 1 c'];

    return (
      <View style={{ marginTop : (Platform.OS) == 'ios' ? 20 : 0 }}>
        <SectionList
          sections={[
            { comment: 'Comment a with subcomponents a', data: a },
            { comment: 'Comment b with subcomponents b', data: b },
            { comment: 'Comment c with subcomponents c', data: c },
          ]}
          renderSectionHeader={ ({section}) => <Text> { section.comment } </Text> }
          renderItem={ ({item}) => <Text> { item } </Text> }
          keyExtractor={ (item, index) => index }
        />
      </View> 
    );
  }

其中部分列表中的data 字段可以是一个组件数组, 希望对您有所帮助。

【讨论】:

  • 我曾短暂尝试过 Flatlist,但由于评论线程的性质,我遇到了在 Flatlists 中嵌套 Flatlists 的问题。也许我做错了。您能否提供一个有关如何将其转换为 FlatList 的高级示例?
  • 我用一个小例子编辑帖子,希望能有所帮助
  • 谢谢!我会试一试。
猜你喜欢
  • 2017-10-22
  • 1970-01-01
  • 2015-03-10
  • 1970-01-01
  • 2018-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-07
相关资源
最近更新 更多