reactjs-如何在React中处理昂贵的函数
发布时间:2022-04-22 13:31:19 274
相关标签: # node.js
我的应用程序中有一个组件,它可以处理两个有些昂贵的函数,但并不总是同时处理。在一个应用程序中,我有一个使用窗口的地球仪。requestAnimationFrame()更新每次请求和旋转时的旋转。当我与组件的其他部分进行交互时,它会平稳运行。
当用户与过滤一系列对象的搜索交互时,问题就会出现。当我输入globe spin动画时,它会平稳运行,一旦调用getItems()函数,它就会停顿/停止,直到getItems()函数完成。地图组件和搜索组件是分开的,但都由更高的组件渲染。当用户在搜索中输入时,没有理由让较高的组件重新渲染,因此地图组件不会重新渲染。这表明问题在于昂贵的功能阻碍了整个应用程序。
当组件挂载在UseEffect挂钩中时,将定义项目列表。保存这些对象的json文件位于公用文件夹中,大约为30MB。当用户搜索时,它会运行getItems()函数。我已经在inputValueChange上设置了一个setTimeout,所以它只在用户可能完成输入后运行。下面是有问题的代码。
import { useEffect, useState, useRef, useCallback, CSSProperties } from 'react'
import { useVirtual } from 'react-virtual'
import { useCombobox, UseComboboxStateChange } from 'downshift'
import { v4 as uuid } from 'uuid';
import { Hint } from 'react-autocomplete-hint';
import MarkerObject from '../../../interfaces/MarkerObjectInterface';
interface Props {
items: Array<MarkerObject>;
onSelect(item: MarkerObject): void;
onHoverEnter: (item: MarkerObject) => void;
onHoverLeave: () => void;
}
const Search = (props: Props) => {
//Initialize the state and refs
const [inputValue, setInputValue] = useState('')
const [items, setItems] = useState<MarkerObject[]>([])
const listRef = useRef<HTMLElement>(null)
//Waits for the user to stop typing for half a second before updating the list of items
//This helps a lot with an unnecescary rerender of the somewhat expensive getItems function
useEffect(() => {
const delayInput = setTimeout(() => {
setItems(getItems(inputValue))
}, 500);
return () => clearTimeout(delayInput);
}, [inputValue])
//Listens for a selected item, then passes that item back to TestMaped Item: ", selectedItem)
const handleSelectedItemChange = (changes: UseComboboxStateChange<any>) => {
props.onSelect(changes.selectedItem)
// props.onHoverLeave(changes.selectItem)
}
//Filters the items based on seach query
function getItems(search: string) {
if (search.length === 0)
return []
return props.items.filter((n) => {
if (n.type === "city") {
const citystatecountry = (n.name + ", " + n.state_name + ", " + n.country_name).toLowerCase()
const citystatecodecountry = (n.name + ", " + n.state_code + ", " + n.country_name).toLowerCase()
const citystatecodecountrycode = (n.name + ", " + n.state_code + ", " + n.country_code).toLowerCase()
const citystate = (n.name + ", " + n.state_name).toLowerCase()
const citystatecode = (n.name + ", " + n.state_code).toLowerCase()
const citycountry = (n.name + ", " + n.country_name).toLowerCase()
const citycountrycode = (n.name + ", " + n.country_code).toLowerCase()
const city = n.name.toLowerCase()
if (citystatecountry.startsWith(search.toLowerCase()))
return true
else if (citystate.startsWith(search.toLowerCase()))
return true
else if (citycountry.startsWith(search.toLowerCase()))
return true
else if (city.startsWith(search.toLowerCase()))
return true
else if (citystatecodecountry.startsWith(search.toLowerCase()))
return true
else if (citystatecodecountrycode.startsWith(search.toLowerCase()))
return true
else if (citystatecode.startsWith(search.toLowerCase()))
return true
else if (citycountrycode.startsWith(search.toLowerCase()))
return true
}
else if (n.type === "state") {
const state = (n.name).toLowerCase()
const statecountry = (n.name + ", " + n.country_name).toLowerCase()
if (state.startsWith(search.toLowerCase()))
return true
else if (statecountry.startsWith(search.toLowerCase()))
return true
}
else {
const country = (n.name).toLowerCase()
if (country.startsWith(search.toLowerCase()))
return true
}
return false
})
}
//Initialies the itemToString funciton to be passed to useCombobox
const itemToString = (item: MarkerObject | null) => (item ? item.display_text : '')
//Initializes the rowVirtualizer using a fixed size function
const rowVirtualizer = useVirtual({
size: items.length,
parentRef: listRef,
estimateSize: useCallback(() => 30, []),
overscan: 1,
})
const {
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
highlightedIndex,
selectedItem,
getComboboxProps,
isOpen,
selectItem
} = useCombobox({
items,
itemToString,
inputValue,
onSelectedItemChange: (changes) => handleSelectedItemChange(changes),
onInputValueChange: (d) => {
console.log(d.inputValue)
if (d.inputValue || d.inputValue === "")
setInputValue(d.inputValue)
},
scrollIntoView: () => { },
onHighlightedIndexChange: ({ highlightedIndex }) => {
if (highlightedIndex)
rowVirtualizer.scrollToIndex(highlightedIndex)
},
})
return (
<div className="" onMouseLeave={() => props.onHoverLeave()}>
<div className="">
<label {...getLabelProps()}>Choose an element:</label>
<div {...getComboboxProps()}>
<input
className={inputCss}
{...getInputProps(
{
type: 'text',
placeholder: 'Enter Place',
})}
/>
</div>
</div>
<ul className="-webkit-scrollbar-track:purple-500"
{...getMenuProps({
ref: listRef,
style: menuStyles,
})}
>
{isOpen && (
<>
<li key="total-size" style={{ height: rowVirtualizer.totalSize }} />
{rowVirtualizer.virtualItems.map((virtualRow) => (
<li
key={uuid()}
{...getItemProps({
index: virtualRow.index,
item: items[virtualRow.index],
style: {
backgroundColor:
highlightedIndex === virtualRow.index
? 'lightgray'
: 'inherit',
fontWeight:
selectedItem &&
selectedItem.id === items[virtualRow.index].id
? 'bold'
: 'normal',
position: 'absolute',
fontSize: '1.2em',
top: 0,
left: 0,
width: '100%',
height: virtualRow.size,
transform: `translateY(${virtualRow.start}px)`,
},
})}
onMouseEnter={() => props.onHoverEnter(items[virtualRow.index])}
onMouseLeave={() => props.onHoverLeave()}
onMouseOut={() => props.onHoverLeave()}
>
{items[virtualRow.index].display_text}
</li>
))}
</>
)}
</ul>
</div>
)
}
export default Search
我还将react virtual与降档结合使用(例如https://www.downshift-js.com/use-combobox#virtualizing-带有“反应虚拟”的项目。这无疑提高了与下拉框交互时的性能,因为动画上几乎没有结巴。
我很好奇我的选择是什么。我还在应用程序中使用firestore。有没有办法分配内存或限制此功能或组件使用的内存?我是否应该使用更好的算法或函数来代替。过滤器()?我应该在公共文件夹中保存这么大的文件吗?
或者我应该把整个json对象放在firestore集合中吗?因为它是30MB,我想在整个文档中进行查询,所以我必须将数据分离到文档中。这似乎会增加大量的文档读取。任何帮助或建议都将不胜感激!
特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报