返回

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,我想在整个文档中进行查询,所以我必须将数据分离到文档中。这似乎会增加大量的文档读取。任何帮助或建议都将不胜感激!

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像