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

如何更新Recoil atom不触发重渲染 解决大量手风琴点击卡顿问题

问题根因

你当前将所有accordion的选中状态存在同一个_SelectedCategories atom中,所有accordion组件都直接订阅了这个完整atom,只要atom值更新,所有依赖它的组件都会触发重渲染,250个accordion每个嵌套15+子组件的情况下,渲染压力自然会很大。另外你当前使用defaultExpanded属于非受控属性,仅在组件初始化时生效,后续atom更新不会同步修改accordion的展开状态,本身就存在逻辑问题。


最优方案:使用atomFamily拆分状态

Recoil的atomFamily可以为每个accordion根据唯一标识propKey生成独立的atom,只有对应标识的atom更新时,才会触发对应的accordion组件重渲染,其余组件完全不受影响。

代码实现:

  1. 定义atomFamily
import { atomFamily } from 'recoil';

// 每个accordion对应一个独立的atom,入参为propKey
export const selectedCategoryState = atomFamily({
  key: 'selectedCategoryState',
  default: false,
});
  1. 单个accordion组件内使用
// 只订阅当前propKey对应的atom状态
const isExpanded = useRecoilValue(selectedCategoryState(propKey));
const setIsExpanded = useSetRecoilState(selectedCategoryState(propKey));

const openAccordion = () => {
  setIsExpanded(prev => !prev);
};

// 注意要用受控属性expanded,不要用defaultExpanded
return (
  <Accordion expanded={isExpanded} id={propKey}>
    <AccordionSummary onClick={openAccordion}>
      { /* Accordion Summary Header */ }
    </AccordionSummary>
    <AccordionDetails>
      { /* Accordion Details Here */ }    
    </AccordionDetails>
  </Accordion>
);

兼容方案:不拆分atom的情况下用selectorFamily按需订阅

如果不想改动原有atom的结构,可以用selectorFamily实现按需订阅,每个组件只取和自己相关的状态值,只有当这个值发生变化时才会触发重渲染:

代码实现:

  1. 定义selectorFamily
import { selectorFamily } from 'recoil';
import { _SelectedCategories } from './your-atom-path';

export const categoryExpandedState = selectorFamily({
  key: 'categoryExpandedState',
  get: (propKey) => ({ get }) => {
    const selected = get(_SelectedCategories);
    return selected?.category === propKey;
  },
  set: (propKey) => ({ set }, newValue) => {
    set(_SelectedCategories, prev => {
      return {
        ...prev,
        category: newValue ? propKey : ''
      };
    });
  }
});
  1. 组件内使用和上面atomFamily的用法完全一致,不需要其他修改。

额外优化点

  • 给Accordion组件包裹React.memo,避免无关的父组件重渲染传递到子组件:
const AccordionItem = React.memo(({ propKey }) => {
  // 组件内部逻辑
});
  • 避免使用JSON.parse(JSON.stringify)做深拷贝,对于单层对象直接用展开运算符{...prev}即可,性能更好,也不会有序列化丢失问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 06:30:04