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);
我可以向您建议几件事,但请记住,我不确定它是否一定会提高您的组件的性能。
- 您的
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>
);
};
- 关于您的
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]);
- 您可以做的另一件事是对搜索结果进行去抖动,因为如果不对其进行去抖动,可能会导致 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>
);
};
我希望其中的一些内容会有所帮助。祝你好运!
我正在尝试实现一个响应式搜索栏,用于过滤纯组件列表(一次最多显示 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);
我可以向您建议几件事,但请记住,我不确定它是否一定会提高您的组件的性能。
- 您的
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>
);
};
- 关于您的
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]);
- 您可以做的另一件事是对搜索结果进行去抖动,因为如果不对其进行去抖动,可能会导致 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>
);
};
我希望其中的一些内容会有所帮助。祝你好运!