You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何搜索输入时useEffect重复执行?如何避免不必要重渲染?

问题:输入框搜索时useEffect为何每次都执行?

相关代码

Sidebar组件

import SearchInput from './searchInput'

const Sidebar = () => {
  const { list } = useList()
  const [searchValue, setSearchValue] = useState<string>('')

  useEffect(() => {
    console.log('list :>> ', list)
  }, [list])

  return (
    <SearchInput searchValue={searchValue} setSearchValue={setSearchValue} />
  )
}

export default React.memo(Sidebar)

useList自定义Hook

export default function useList() {
  let { data } = useQuery(GET_LIST)
  const list = _.cloneDeep(data)?.menuTree?.filter((i) => i.parentId === null) || []

  const otherMenus = data?.menuTree || []

  return { list, otherMenus }
}

SearchInput组件

const SearchInput = ({ searchValue, setSearchValue }) => {
  return (
    <div>
        <Input
          value={searchValue}
          onChange={(e) => setSearchValue(e.target.value)}
        />
    </div>
  )
}

export default React.memo(SearchInput)

已尝试组件memo化和缓存useQuery返回的数据,但均无效,寻求解决方法。


原因分析

  • 输入框输入时,searchValue状态更新,触发Sidebar组件重渲染。React.memo只能阻止props变化导致的重渲染,无法阻止组件内部状态变化引发的重渲染,所以Sidebar每次都会重新执行。
  • 核心问题在于useList Hook:每次组件重渲染时,list都是通过_.cloneDeep+filter生成的新数组,数组的引用地址每次都不一样。而useEffect通过引用比较判断依赖是否变化,所以每次重渲染都会触发useEffect执行。
  • 即使useQuery返回的data是稳定的,每次对其深拷贝并过滤都会生成新的数组引用,导致list永远是新值。

解决方案

1. 用useMemo缓存计算结果

修改useList Hook,使用useMemo包裹list和otherMenus的计算逻辑,确保只有当data变化时才重新生成数组,而非每次组件重渲染都创建新数组:

import { useMemo } from 'react';

export default function useList() {
  const { data } = useQuery(GET_LIST);
  
  const list = useMemo(() => {
    // 仅当data变化时,才重新执行深拷贝和过滤
    return _.cloneDeep(data)?.menuTree?.filter((i) => i.parentId === null) || [];
  }, [data]);

  const otherMenus = useMemo(() => {
    return data?.menuTree || [];
  }, [data]);

  return { list, otherMenus };
}

2. 移除不必要的_.cloneDeep(可选)

如果useQuery返回的data是只读的,不会被后续逻辑修改,可去掉_.cloneDeep减少性能开销,再配合useMemo缓存过滤结果:

const list = useMemo(() => {
  return data?.menuTree?.filter((i) => i.parentId === null) || [];
}, [data]);

补充说明

Sidebar的React.memo无法阻止自身状态变化引发的重渲染,但通过useMemo稳定list的引用后,即使Sidebar重渲染,list的引用地址不变,useEffect的依赖不会触发变化,也就不会重复执行了。


内容的提问来源于stack exchange,提问作者Mir Stephen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:42:29