useSelector 和 useEffect 重新渲染优化

Posted

技术标签:

【中文标题】useSelector 和 useEffect 重新渲染优化【英文标题】:useSelector and useEffect rerender optimization 【发布时间】:2021-12-30 22:28:32 【问题描述】:

我有一个表组件,其中 tableRows 存储在 useState 中。

我在表格组件之外还有搜索器组件。

当搜索器中的数据发生变化时,tableRows 会在 useEffect 中更新。

而且效果很好,但是会导致两次重新渲染。我明白为什么。第一次因为 useSelector 重新渲染,第二次因为 useEffect 有 useSelector 值作为依赖。

但是如何避免一次重新渲染。我希望它在 tableRows 更改时重新呈现,而不是在 searcher 更改时重新呈现。

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

【问题讨论】:

【参考方案1】:

一种可能的解决方案是记忆:

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

另一种解决方案是将 tableRows 和搜索查询放在 redux 存储中,并通过异步中间件同时更新它们。

【讨论】:

【参考方案2】:

它会重新渲染,因为您的状态正在改变,这是预期的结果。这是因为 tableRows 是在 useEffect 内部设置的(在 effect 内部有一个 setTableRows,这取决于搜索者)。

不确定组件的使用方式,因为您在道具中传递了一些初始行并重置组件内的行(setTableRows),但从您的评论看来,您并不真正关心搜索器中的更改.完成您想要的解决方案是将行作为道具传递并从组件中删除状态,这样您的组件将只关心行而不关心搜索者。

无论如何,优化重新渲染并不是最好的方法,除非您的渲染速度很慢。此外,在 useEffect 中定义异步调用更安全,这样更容易确保所有需要的依赖项都在依赖项数组中。

【讨论】:

以上是关于useSelector 和 useEffect 重新渲染优化的主要内容,如果未能解决你的问题,请参考以下文章

如何更新反应表的数据

使用 Ionic 从另一个页面重定向时不会触发 useEffect

在Redux中使用useSelector和useDispatch

在Redux中使用useSelector和useDispatch

为啥 useSelector 不包含任何数据?

useSelector 钩子和重新渲染