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

React输入搜索文本时结果列表重复渲染问题该如何解决?

问题根因
  • 你将Asset组件定义在了AssetListTool组件内部,每次父组件因为searchText更新重渲染时,Asset组件的引用都会被重新创建,你给它加的React.memo完全失效,导致所有图片项每次都跟着重渲染
  • onSearchTextChanged的useCallback错误添加了searchText作为依赖,每次输入都会生成新的回调引用,进一步加剧不必要的渲染
  • 所有状态都维护在父组件,输入状态更新时必然触发父组件整体重渲染,哪怕图片列表的依赖没有任何变化
最优解决方案

优先采用拆分组件的方案,完全可以解决你的问题,不会出现你担心的“子组件重渲染带动父组件重渲染”的问题:子组件自身的内部状态更新只会触发自身重渲染,只有子组件调用父组件传入的方法修改父组件状态时,才会触发父组件重渲染。
具体优化步骤:

  1. 抽离SearchBar子组件,内部独立维护searchText输入状态,仅在用户回车/点击搜索按钮时,才调用父组件传入的onSearch回调提交搜索词
  2. 抽离AssetList和Asset子组件,用React.memo包裹,仅当assets属性变化时才触发重渲染
  3. 修复原代码的闭包隐患,比如useEffect中更新assets用函数式写法,修正useCallback的依赖

优化后完整代码:

import { memo, useState, useCallback, useEffect } from 'react'

// 抽离到父组件外部的Asset项组件,memo生效
const Asset = memo(function ({ asset }) {
  return (
    <div className="asset">
      <img src={asset.thumbnail} alt={asset.title} />
    </div>
  )
})

// 图片列表组件,memo包裹,仅assets变化时重渲染
const AssetList = memo(function ({ assets }) {
  if (!assets?.length) return null
  return (
    <div>
      {assets.map(asset => <Asset key={asset.id} asset={asset} />)}
    </div>
  )
})

// 搜索栏组件,内部维护输入状态,输入时仅自身重渲染
function SearchBar({ onSearch }) {
  const [searchText, setSearchText] = useState('')

  const onSearchTextChanged = useCallback((e) => {
    setSearchText(e.target.value)
  }, [])

  const onSearchKeyPressed = useCallback((e) => {
    if (e.key === 'Enter') {
      handleSearch()
    }
  }, [searchText, onSearch])

  const handleSearch = useCallback(() => {
    onSearch(searchText.trim())
  }, [searchText, onSearch])

  return (
    <div>
      <div>
        <input 
          onChange={onSearchTextChanged} 
          onKeyPress={onSearchKeyPressed} 
          value={searchText}
          type="text"
        />
      </div>
      <div>
        <button onClick={handleSearch}>
          <Icon icon={faSearch}/>
        </button>
      </div>
    </div>
  )
}

function AssetListTool () {
  const [assets, setAssets] = useState([])
  const [searchTerm, setSearchTerm] = useState('')

  // 搜索回调用useCallback包,保证引用稳定,不会导致SearchBar不必要重渲染
  const handleSearch = useCallback((keyword) => {
    setAssets([])
    setSearchTerm(keyword)
  }, [])

  useEffect(() => {
    const params = {
      limit: 30,
      sort: 'title:desc',
      searchTerm
    }
    const matchingAssets = assetApi.getAllAssets({...params })
    Promise.all([matchingAssets]).then(responses => {
      // 用函数式更新避免闭包问题,不需要依赖外部assets
      setAssets(prev => prev.concat(responses[0].items))
    })
  }, [searchTerm])

  return (
    <>
      <SearchBar onSearch={handleSearch} />
      <AssetList assets={assets} />
    </>
  )
}

export default AssetListTool
优化效果
  • 搜索框输入时,仅SearchBar组件自身重渲染,父组件和图片列表完全不会触发重渲染
  • 仅当搜索提交、searchTerm变化触发新数据返回后,AssetList才会因为assets更新重渲染
  • 列表中的Asset项仅当自身asset属性变化时才会重渲染,不会跟着列表整体无差别重渲染

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:54:00