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

为什么自定义Hook中useState存储的items在searchItems函数内为null

问题根因与解决方案

核心问题1:事件绑定写法错误

你在DataList组件中给按钮绑定点击事件的写法存在逻辑错误:

return <button onClick={runSearch('searchTerm')}

以上写法会在组件渲染阶段直接执行runSearch函数,而非点击按钮时触发。此时useEffect中发起的API请求还未返回结果,items自然还是初始的null值。

修复方案

修改为点击时才执行的函数引用形式:

return <button onClick={() => runSearch('searchTerm')}>搜索</button>

核心问题2:Hooks闭包捕获陷阱

就算修复了事件绑定问题,你还是会碰到items为null的情况,本质是React Hooks的闭包捕获特性导致的:

  1. 首次渲染DataList时,useStore执行生成初始的store对象,其中的searchItems函数捕获了当前作用域下的初始值items = null
  2. API请求返回后调用setItems,触发useStore重执行,生成新的store对象和新的searchItems函数,新函数捕获了更新后的items值
  3. 但你首次渲染时创建的runSearch函数,引用的始终是首次生成的旧store里的旧searchItems函数,该函数永远只能拿到null的items值
    这也是你给searchItems加useCallback也没用的原因:虽然items更新后会生成新的searchItems,但外层始终没用到这个新函数。

最优修复方案

在useStore中用useRef存储items的最新值,避开闭包的影响:

export const useStore = () => {
  const [items, setItems] = useState(null)
  // 用ref存储items最新值,不受闭包影响
  const itemsRef = useRef(items)

  // 每次items更新同步到ref
  useEffect(() => {
    itemsRef.current = items
  }, [items])

  const setItemsFromApi = async () => {
    // 调用API拿数据
    const data = await fetchApi()
    setItems(data)
  }

  const searchItems = (query) => {
    // 直接从ref拿最新值,加可选链避免空值报错
    return itemsRef.current?.filter(item => item.includes(query)) ?? []
  }

  console.log(items) // 此处能拿到更新后的值是因为每次重渲染都会执行打印,拿到的是当前作用域的最新items

  return {setItemsFromApi, searchItems}
}

其他可选修复方案

如果你不想用useRef,也可以把store加入runSearch的依赖,确保runSearch始终引用最新的searchItems:

const runSearch = useCallback((query) => {
  store.searchItems(query)
}, [store])

该方案虽然能解决问题,但每次items更新都会生成新的runSearch函数,性能不如useRef方案。

内容的提问来源于stack exchange,提问作者D. Messier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 21:48:03