【问题标题】:Performance problems with long array lists in the React stateReact 状态下长数组列表的性能问题
【发布时间】:2020-03-17 15:20:27
【问题描述】:

我有一个应用程序请求 API 并有数千个结果。该 API 使用 limit offset 类型的分页。也就是说,我将限制(我想带来多少条记录)和偏移量(我想从哪条记录带来)作为参数传递。

问题是数据库每天有数千个结果。大约 150k 的结果。也就是说,每当我更新 React 的状态以添加新结果时,我都会遇到性能问题,因为 React 会重新渲染所有数据。

以下是部分代码。

async getHits(filters, resetContent = false) {
        if(resetContent) {
            this.setState({
                isLoading: true
            })
        }
        try {
            const { data: { result } } = await API_CONFIG.get(`/hit${filters}`);

            const formattedHits = formatHits(result);

            let current;
            if(resetContent) {
                current = [...formattedHits];
            } else {
                current = [...this.state.hits, ...formattedHits]
            }

            this.setState(prevState => ({
                hits: current,
                currentOffset: resetContent ? 1 + DEFAULT_PAGINATION : prevState.currentOffset + DEFAULT_PAGINATION
            }));

        } catch (e) {
            this.setState({
                hits: [],
                error: 'Houve um erro durante a requisição. Por favor, recarregue e tente novamente.'
            });
        } finally {
            this.setState({
                isLoading: false,
                isLoadMoreLoading: false
            })
        }
    }

例如,当我点击加载更多时,我将状态更改为将'isLoadMoreLoading'设置为true,因此我将文本从Carregar mais(葡萄牙文加载更多)更改为 Carregando(葡萄牙语加载)。这个过程需要很长时间,因为 React 会再次更新整个状态。

我在这里渲染内容:

const areEqual = (nextProps, prevProps) => {
    if(nextProps === prevProps) {
        return true
    };

    return false;
};

const TableResults = props => {
    const { hits, isLoading, error } = props;
    return (
        <Table>
            <thead>
            <tr>
                <TableHeader>Data</TableHeader>
                <TableHeader>Parceiro</TableHeader>
                <TableHeader>ID Arquivo</TableHeader>
                <TableHeader>Nome do Cliente</TableHeader>
                <TableHeader>Regra Afetada</TableHeader>
                <TableHeader>Valor ME</TableHeader>
                <TableHeader>Merchant Final</TableHeader>
                <TableHeader>País Merchant Final</TableHeader>
                <TableHeader>Gerador do Hit</TableHeader>
            </tr>
            </thead>

            <tbody>
            {isLoading && (
                <tr>
                    <td className="content-not-allowed" colSpan={9}>
                        <Loader />
                    </td>
                </tr>
            )}

            {!isLoading && error && (
                <tr>
                    <td className="content-not-allowed not-found" colSpan={9}>
                        <span>{error}</span>
                    </td>
                </tr>
            )}

            {!isLoading && !hits.length && !error && (
                <tr>
                    <td className="content-not-allowed not-found" colSpan={9}>
                        <span>Sem resultados, revise sua busca.</span>
                    </td>
                </tr>
            )}

            {!isLoading && !!hits.length && !error && (
                hits.map(hit => (
                    <tr onClick={e => {
                        e.preventDefault();
                        props.history.push('/compliance/operations/'+hit.hit_id)
                    }} key={hit.hit_id}>
                        <td>{hit.created_at || '-'}</td>
                        <td>{hit.partner || '-'}</td>
                        <td>{hit.file_id || '-'}</td>
                        <td>{hit.cliente_nome || '-'}</td>
                        <td>{renderHitGenerator(hit.compliance_integration).description}</td>
                        <td>{hit.valorme || '-'}</td>
                        <td>{hit.merchant_nome_final || '-'}</td>
                        <td>{hit.merchant_pais_final || '-'}</td>
                        <td>{renderHitGenerator(hit.compliance_integration).name}</td>
                    </tr>
                ))
            )}
            </tbody>
        </Table>
    )
}

export default withRouter(React.memo(TableResults, areEqual));

我在互联网上查看了针对这么多数据优化结果,但没有找到令人满意的结果。

我怎样才能用这么多的数据更新 React 的状态并且仍然是一个执行者。

谢谢。

【问题讨论】:

  • 看起来像以下任何一个(或多个)导致性能问题的问题: 1. 您确定要向 API 调用发送限制和偏移量吗? 2. 你设置了两次状态。在您的结果返回后一次,一次在finally 块中。您可以一口气完成。与 error 块相同。 3. 请检查您是否在重复组件中使用了key 属性。这对于防止重新渲染数据很重要。
  • 你在同一个组件内渲染数组组件吗?
  • 感谢您的回复。 @Ashwin回应:1.是的,我在变量“过滤器”中发送限制。 2.我设置了2次状态,因为无论我是否设法带来API响应,我都必须删除加载程序。 3. 是的,我为每个渲染行传递 1 个密钥。 Burak 我用 React.memo 在另一个组件中渲染。我将把那段代码放在问题中。我的问题是不带数据。但我必须向我的状态添加越来越多的数据,而且它越来越重。
  • First Memo 没有用,因为 areEqual 使用 === 来检查对象实例是否相等。它总是错误的。
  • 另一件事,您可以删除 isLoading 和错误检查。使它们与 hits.length 分开。主要显示表内的加载程序和错误使用表外的一些 div 或弹出微调器在视觉上看起来更快,甚至后端慢

标签: javascript reactjs


【解决方案1】:

确保数组中的每个元素都映射到唯一且确定的键。一旦这是真的,您可以使用shouldComponentUpdate 来确保组件只有在它们不同时才会重新渲染。

您还可以查看 react-infinite 之类的库来限制一次渲染的项目数量以提高性能。

最后,由于您使用的是 React Router,因此使用渲染方法而不是组件方法来防止重新渲染非常重要。有关更多信息,请参阅this SO post

【讨论】:

    猜你喜欢
    • 2017-05-11
    • 2010-12-17
    • 2021-12-03
    • 1970-01-01
    • 2019-02-13
    • 2020-11-05
    • 1970-01-01
    • 2017-05-02
    • 2018-05-20
    相关资源
    最近更新 更多