React表格显示图片行时加载缓慢

5

我是React的新手,正在尝试从API获取数据并构建表格。但现在我想把数据渲染到表格中,我的问题是其中一列是来自URL的标志 index.php?Action=GetLogo&id=51。结果数量超过300条,即使我已经使用了分页,表格渲染仍然很慢,尤其是logo列。虽然所有数据都加载完成,但我可以看到每个标志都逐行渲染,给用户带来缓慢的体验。

有没有什么办法让React解决这个问题?请指点我如何解决这个问题。

谢谢大家。

更新 有人建议我使用同步/等待函数来加载图片。然后我更新了代码。然而,我的主要问题仍然存在:加载数据,特别是标志列。虽然所有数据都已呈现,但标志列却没有,然后每个标志开始逐个渲染,看起来非常缓慢。我认为async/await函数会缓解这个问题。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import FormData from 'form-data';

    class FilterableSupplierTable extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                suppliers: []
            }
        }

        componentDidMount() {
            let formData = new FormData();
            formData.append('AjaxMethodName', 'GetSuppliers');
            const options = {
                method: 'POST',
                headers: {
                    'Accept': '*/*'
                },
                body: formData
            };
            fetch(`?Model=route_to_controller`, options)
                .then(response => response.json())
                .then(data => {
                    this.setState({ suppliers: JSON.parse(data.response_data) })
                });
        }

        async getLogos(suppliers) {
            return await Promise.all(
                suppliers.map(async supplier => {
                    supplier.new_logo = !!supplier.has_logo ?
                        <img style={{maxWidth: "100px"}} src={supplier.logo} alt={supplier.supplier_id} /> :
                        <i className="icon icon-warning no_logo">&nbsp;No Logo</i>;
                    return supplier;
                });
            );
        }

        render() {
            const rows = [];
            const suppliers = this.state.suppliers;
            this.getLogos(suppliers)
                .then(results => {
                    results.map(supplier => {
                        rows.push(
                            <tr>
                                {/* supplier.logo is index.php?Action=GetLogo&id=51, etc */}
                                <td><img src={supplier.new_logo} /></td>
                                <td>{supplier.name}</td>
                            </tr>
                        );
                    });
                });

            return (
                <table>
                    <thead>
                        <tr>
                            <th colSpan="4">Suppliers</th>
                        </tr>
                        <tr>
                            <th>Logo</th>
                            <th>Name</th>
                        </tr>
                    </thead>
                    <tbody>{rows}</tbody>
                </table>
            );
        }
    }
    ReactDOM.render(
        <FilterableSupplierTable />,
        document.getElementById('suppliers_list')
    );

有时候图片的大小会增加开发环境的加载时间,你确定这些图片已经进行了网页优化吗? - Hadi Pawar
@AdrianPascu. 你能给我一个例子,我如何使用这些代码块吗? - ivantxo
Async/Await是JS的一种特性,用于等待Promise的响应。你应该了解如何使用它。官方MDN文档非常好。至于Suspense块,这里有一个很好的例子https://blog.logrocket.com/react-suspense-for-data-fetching/。它使用hooks而不是基于类别的组件,但是hooks真的不难理解,并且在react文档中有非常好的记录。如果您不想切换到hooks,所有内容都可以映射到类组件。 - Adrian Pascu
@GalAbra。不,我想等待并加载标志,不要显示任何内容,也许只是一个旋转的加载轮。然后,当所有标志都加载完成后,再渲染所有内容。这种情况下是否可行?这是最好的做法吗? - ivantxo
@GalAbra 抱歉,我的数组仍然是空的。所以,没有任何东西被渲染出来。我将这个数组打印到控制台上,当我展开它时,它有所有的行,但是当折叠时似乎是空的。 - ivantxo
显示剩余6条评论
2个回答

7

您的问题可以通过更新“全局加载状态”的组件来解决。

只有在所有图像完成加载后,它们才会一起显示:

function MyImage(props) {
  const onLoad = () => {
    props.onLoad();
  };

  return (
    <div>
      {props.isCurrentlyLoading && <div>Loading</div>}
      <img
        src={props.src}
        onLoad={onLoad}
        style={
          props.isCurrentlyLoading
            ? { width: "0", height: "0" } // You can also use opacity, etc.
            : { width: 100, height: 100 }
        }
      />
    </div>
  );
}

function ImagesBatch() {
  const [loadedImagesCounter, setLoadedImagesCounter] = useState(0);
  const [isAllLoaded, setIsAllLoaded] = useState(false);

  const updateLoading = () => {
    if (loadedImagesCounter + 1 === imagesUrls.length) {
      setIsAllLoaded(true);
    }
    setLoadedImagesCounter(prev => prev + 1);
  };

  return (
    <div>
      {imagesUrls.map((url, index) => {
        return (
          <MyImage
            key={url}
            src={url}
            index={index + 1}
            isCurrentlyLoading={!isAllLoaded}
            onLoad={updateLoading}
          />
        );
      })}
    </div>
  );
}

您可以在此处查看完整代码(最好打开控制台),我使用了一张约6MB的任意图片作为示例。


我正在检查,谢谢。如果我使用你的函数,甚至不需要我的 getLogos(async/await)函数吗? 我的图片只有几个KB大小:p - ivantxo
@ivantxo 没错。你只需要过滤客户端数组,以确保只计算具有有效图像 URL 的客户端数。 - GalAbra
我如何才能为所有元素设置一个单独的“加载中”消息,而不是每个图像都有一个“加载中”消息? - ivantxo
是的,只需从 MyImage 中删除它并添加到 ImagesBatch 中。 - GalAbra
1
我在React方面还有很长的路要走... 感谢@GalAbra - ivantxo

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接