Home > Blockchain >  Reactjs responsively rendering list
Reactjs responsively rendering list

Time:01-03

I am trying to implement a responsive search bar that filters a list of pure components (max 100 components show at once). However, there is a half-second freeze between typing the initial letter and having it show up in the search bar with the rendered list of components below it. I believe it is the number of rendered components that causes this delay since limiting the render amount to ~20 makes the response near-instant.

What are some options for me to minimize this delay and create an almost instant render?

I have looked at lazy-loading, but I can only find methods that dynamically load based on the user scrolling. Is there a method that splits the rendering of the list over time so there isn't one long pause?

I have also looked at React.memo but the delay is when I go from 0 -> 100 components so caching after the initial loading doesn't really help. Is there way of preloading these components into a cache?

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

CodePudding user response:

There are several things I can suggest to you, but bear in mind that I am not really sure if it will definitely improve the performance of your components.

  1. Your SearchPage component is coupled to handle the updating of your searchResults data. I think that this could potentially cause performance issues and from a software design principle (precisely the Single-Responsibility principle) it looks like it's badly designed. What I would do in this case is that I would move the state to your DataListTable component and instead pass the search value as a property to it:
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>
  );
};

Then your SearchPage could look like this:

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

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

  return (
    <div>
      <SearchBar onInput={onInput} />
      <DataListTable search={search} />
    </div>
  );
};
  1. About your dataMap object: I think that you should map the objects after you receive the results from your API endpoint. You shouldn't do data mapping directly inside of the component. You could do it inside of your searchIndex.search method. A hypothetical example would be:

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

And then simply call it inside of your DataListTable component:

import * as searchIndex from './searchService';

useEffect(() => {
  searchIndex.search(search, { limit: 100 }).then((data) => setSearchResults(data));
}, [search]);
  1. One another thing you could do is to debounce the search result, because if you don't debounce it, it could cause 2 drawbacks:
  • Your API is burdened with unnecessary calls
  • Your DataListTable component is burdened with unnecessary re-renders

For the purpose of debouncing, you could use this useDebounce hook, taken from 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;
};

And then just use the useDebounce inside of your SearchPage component:

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

I hope that some of these things will prove to be helpful. Good luck!

  • Related