Search code examples
javascriptreactjsreduxuse-effectrerender

useSelector and useEffect rerender optimization


I have table component with tableRows stored in useState.

Also I have searcher component outside of table component.

When data inside of searcher changes, tableRows updates inside useEffect.

And it works good, but it causes two rerender. And i understand why. First rerender because of useSelector, and the second because useEffect have useSelector value as dependency.

But how to avoid one rerender. I want it to rerenders when tableRows changes, but not when searcher changes.

const CatalogTable: React.FC<CatalogTableProps> = ({rows}) => {
    const [tableRows, setTableRows] = React.useState(rows)
    const searcher = useSelector(getTableSearcher, shallowEqual)

    const getData = async () => {
        const {data} = await CatalogService.getAllCatalogProducts({page: 1, searcher: searcher})
        setTableRows(data.products)
    }


    React.useEffect(() => {
        if(searcher)
            getData()
    }, [searcher])

    return (
        <>
            <div className={styles.defaultTable}>
                <Table
                    headers={headers}
                    label="Products catalog"
                    rows={tableRows}
                    total={total}
                    pagination
                    addButton
                    editButtons
                    searcher
                    getPage={(page: number) => nextPage(page)}
                    type='catalog'
                />
            </div>
        </>
    )
}

export default CatalogTable


Solution

  • One of the possible solutions is memoization:

    const CatalogTable: React.FC<CatalogTableProps> = ({rows}) => {
        const [tableRows, setTableRows] = React.useState(rows)
        const searcher = useSelector(getTableSearcher, shallowEqual)
    
        const getData = async () => {
            const {data} = await CatalogService.getAllCatalogProducts({page: 1, searcher: searcher})
            setTableRows(data.products)
        }
    
    
        React.useEffect(() => {
            if(searcher)
                getData()
        }, [searcher])
    
        const result = useMemo( () =>
            <>
                <div className={styles.defaultTable}>
                    <Table
                        headers={headers}
                        label="Products catalog"
                        rows={tableRows}
                        total={total}
                        pagination
                        addButton
                        editButtons
                        searcher
                        getPage={(page: number) => nextPage(page)}
                        type='catalog'
                    />
                </div>
            </>, [tableRows]
        );
    
      return result;
    }
    
    export default CatalogTable
    

    another solution is putting both tableRows and search query inside redux store and update them simultaneously through async middleware.