Reactjs 响应式渲染列表

Reactjs responsively rendering list

我正在尝试实现一个响应式搜索栏,用于过滤纯组件列表(一次最多显示 100 个组件)。但是,在输入首字母和让它显示在搜索栏中以及下方呈现的组件列表之间有半秒的冻结。我认为是渲染组件的数量导致了这种延迟,因为将渲染量限制在 ~20 会使响应接近即时。

我有哪些选择可以最大限度地减少这种延迟并创建几乎即时的渲染?

我看过延迟加载,但我只能找到根据用户滚动动态加载的方法。有没有一种方法可以随着时间的推移拆分列表的呈现,这样就不会出现长时间的停顿?

我也查看了 React.memo,但延迟是从 0 -> 100 个组件开始的,因此在初始加载后进行缓存并没有真正帮助。有没有办法将这些组件预加载到缓存中?

const SearchPage = ({ dataMap }) => {
  const [searchResults, setSearchResults] = useState([]);

  const onInput = (e) => {
    // near instant response when limit is ~20
    setSearchResults(searchIndex.search(e.target.value, { limit: 100 }));
  };

  return (
    <div>
      <SearchBar onInput={onInput} />
      <DataListTable
        dataList={searchResults.map((dataId) => (dataMap[dataId]))}
      />
    </div>
  );
};


const DataListTable = ({ dataList }) => (
  <table className="table">
    <thead>
      <tr>
        <th>Inputs</th>
        <th>Outputs</th>
        <th>Requirement</th>
      </tr>
    </thead>
    <tbody>
      {dataList.map((data) => (
        <DataRow data={data} key={data.dataId} />
      ))}
    </tbody>
  </table>
);

const DataRow = ({ data }) => {}
export default memo(DataRow);

我可以向您建议几件事,但请记住,我不确定它是否一定会提高您的组件的性能。

  1. 您的 SearchPage 组件已耦合处理您的 searchResults 数据的更新。我认为这可能会导致性能问题,并且从软件设计原则(准确地说是 Single-Responsibility principle)看来它的设计很糟糕。在这种情况下,我会做的是将 state 移动到您的 DataListTable 组件,而不是将搜索值作为 属性 传递给它:
const DataListTable = ({ search }) => {
  const [searchResults, setSearchResults] = useState([]);

  useEffect(() => {
    setSearchResults(searchIndex.search(search, { limit: 100 }));
  }, [search]);

  return (
    <table className="table">
      <thead>
        <tr>
          <th>Inputs</th>
          <th>Outputs</th>
          <th>Requirement</th>
        </tr>
      </thead>
      <tbody>
        {searchResults.map((data) => (
          <DataRow data={data} key={data.dataId} />
        ))}
      </tbody>
    </table>
  );
};

那么你的 SearchPage 可能看起来像这样:

const SearchPage = () => {
  const [search, setSearch] = useState('');

  const onInput = (e) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <SearchBar onInput={onInput} />
      <DataListTable search={search} />
    </div>
  );
};
  1. 关于您的 dataMap 对象:我认为您应该在从 API 端点收到结果后映射对象。您不应该直接在组件内部进行数据映射。您可以在 searchIndex.search 方法中执行此操作。一个假设的例子是:

searchService.js

export const search = async (term, options) => {
  const response = await api.getDataListTableData(term, options);
  const mappedResponse = response.map((x) => ({
    id: x._id,
    name: `${x.first_name} ${x.last_name}`,
  }));

  return mappedResponse;
};

然后只需在 DataListTable 组件中调用它:

import * as searchIndex from './searchService';

useEffect(() => {
  searchIndex.search(search, { limit: 100 }).then((data) => setSearchResults(data));
}, [search]);
  1. 您可以做的另一件事是对搜索结果进行去抖动,因为如果不对其进行去抖动,可能会导致 2 个缺点:
  • 您的 API 被不必要的电话压得喘不过气来
  • 您的 DataListTable 组件承受着不必要的重新渲染

为了消除抖动,您可以使用这个 useDebounce 钩子,取自 this article:

const useDebounce = (value, timeout) => {
    // Save a local copy of `value` in this state which is local to our hook
    const [state, setState] = useState(value);

    useEffect(() => {
        // Set timeout to run after delay
        const handler = setTimeout(() => setState(value), timeout);

        // clear the setTimeout listener on unMount
        return () => clearTimeout(handler);
    }, [value, timeout]);

    return state;
};

然后只需在 SearchPage 组件中使用 useDebounce

const SearchPage = () => {
  const [search, setSearch] = useState('');
  const debouncedSearchQuery = useDebounce(search, 500);

  const onInput = (e) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <SearchBar onInput={onInput} />
      <DataListTable search={debouncedSearchQuery} />
    </div>
  );
};

我希望其中的一些内容会有所帮助。祝你好运!