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

实现局部展开/收起功能 避免React组件整体重渲染的解决方案

解决Show More/Show Less功能的组件重渲染问题

需求与问题

  • 需求:为每个<div>和<p>实现独立的show more/show less功能,点击对应按钮仅展开当前内容,避免整体组件重渲染
  • 当前问题:使用useState在父组件维护状态数组时,点击按钮会触发所有子组件重渲染,导致性能损耗

现有代码示例

父组件

const [arr, setArr] = useState([])
const onCl = (e) => {
  setArr((prev) => [...prev, e.target.value])
}
return (
  divarray.map((i, j) => {
    console.log("tdic") // 每次点击都会触发所有子组件的这个日志,说明全量重渲染
    return (
      <Commentbox divarr={arr[j]} value={j} oncl={(e) => onCl(e)} />
    )
  })
)

子组件(Commentbox)

return (
  <div>
    div{j}
    {/* some icons here */}
    {divarr && <div> right side div </div>}
    <button onClick={(e) => {oncl(e)}} value={j} >see more</button>
  </div>
)

问题分析

父组件用数组arr维护所有子组件的展开状态,每次更新都会生成新数组,导致所有Commentbox子组件因为divarr或oncl属性变化触发重渲染——即使只有一个子组件的状态改变,其他无关子组件也会跟着重新渲染。

解决方案

方案1:让子组件独立管理自身状态

把展开状态移到子组件内部,每个子组件自己维护是否展开,父组件完全不参与状态管理,从根源避免父组件更新导致的全量重渲染。

修改后的子组件:

const Commentbox = ({ j }) => {
  const [isExpanded, setIsExpanded] = useState(false)
  
  return (
    <div>
      div{j}
      {/* some icons here */}
      {isExpanded && <div> right side div </div>}
      <button onClick={() => setIsExpanded(!isExpanded)} >
        {isExpanded ? 'see less' : 'see more'}
      </button>
    </div>
  )
}

父组件简化为:

return (
  divarray.map((i, j) => {
    return <Commentbox key={j} j={j} />
  })
)

这种方式下,每个子组件的状态变化只会触发自身重渲染,完全不影响其他子组件和父组件,性能最优。

方案2:React.memo+父组件状态优化(适合需父组件同步状态的场景)

如果必须在父组件维护状态,可通过以下方式减少重渲染:

  1. 用对象而非数组存储状态,避免每次更新生成新数组导致所有子组件的divarr属性变化
  2. 用useCallback缓存点击回调,避免每次父组件渲染生成新函数
  3. 用React.memo包裹子组件,只有当props真正变化时才重渲染

修改后的父组件:

const [expandedMap, setExpandedMap] = useState({})
// 用useCallback缓存回调,避免每次父组件渲染生成新函数
const onCl = useCallback((index) => {
  setExpandedMap(prev => ({
    ...prev,
    [index]: !prev[index]
  }))
}, [])

return (
  divarray.map((i, j) => {
    return (
      <Commentbox 
        key={j}
        isExpanded={expandedMap[j]} 
        index={j} 
        oncl={onCl} 
      />
    )
  })
)

修改后的子组件:

// 用React.memo包裹,只有props变化时才重渲染
const Commentbox = React.memo(({ isExpanded, index, oncl }) => {
  console.log(`渲染div${index}`)
  return (
    <div>
      div{index}
      {/* some icons here */}
      {isExpanded && <div> right side div </div>}
      <button onClick={() => oncl(index)} >
        {isExpanded ? 'see less' : 'see more'}
      </button>
    </div>
  )
})

这种方式下,只有被点击的子组件的isExpanded属性会变化,其他子组件的props不变,因此不会重渲染。

方案3:原生DOM操作(适合非复杂交互场景)

如果不需要React状态同步,可直接用原生DOM控制内容显示隐藏,完全绕过React的渲染机制:

const Commentbox = ({ j }) => {
  const contentRef = useRef(null)
  
  const toggleContent = () => {
    if (contentRef.current) {
      contentRef.current.style.display = contentRef.current.style.display === 'none' ? 'block' : 'none'
    }
  }
  
  return (
    <div>
      div{j}
      {/* some icons here */}
      <div ref={contentRef} style={{display: 'none'}}> right side div </div>
      <button onClick={toggleContent} >see more</button>
    </div>
  )
}

这种方式完全不涉及React状态更新,因此不会触发任何组件重渲染,性能最好,但缺点是无法和React状态联动,适合简单的显示隐藏场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:55:24