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

Mobx如何实现仅在computed值变化时才重新渲染对应列表项?

问题根因

  1. 首次全量重渲染的原因:你在每个列表项组件内部直接读取了store.playingVideo这个可观察属性,播放切换时playingVideo返回值变更,会触发所有依赖它的组件重渲染。你在渲染函数内临时创建computed并调用get()的写法没有缓存效果,每次渲染都会新建computed实例,无法避免依赖收集。
  2. 后续列表不更新的原因:大概率是store.player或store.player.videoId没有被标记为可观察属性,Mobx无法监听到值的变更,所以observer包裹的Playlist组件不会触发重渲染。

解决方案

方案一:使用createTransformer实现单组件精准更新

Mobx的createTransformer可以缓存每个id对应的激活状态计算结果,只有当计算结果发生变化时才会触发对应组件更新,其余组件完全不会重渲染。

import { makeAutoObservable, createTransformer } from "mobx";
import { observer } from "mobx-react-lite";

// 首先确认store所有属性已标记为可观察
class AppStore {
  player = {
    videoId: null,
    isStopped: true
  };
  videos = {};
  playlist = [];

  constructor() {
    makeAutoObservable(this); // 必不可少,让Mobx监听属性变化
  }

  get playingVideo() {
    if (!this.player.videoId || this.player.isStopped) return null;
    return this.videos[this.player.videoId];
  }
}

// 创建转换器,缓存每个视频id对应的激活状态
const getIsMediaActive = createTransformer((videoId, store) => {
  return store.player.videoId === videoId && !store.player.isStopped;
});

const DraggableMediaItem = observer(({ video, index }) => {
  const store = useAppStore();
  // 仅当前视频的激活状态变化时,才会触发组件重渲染
  const isMediaActive = getIsMediaActive(video.id, store);

  console.log("RENDER", video.id); // 仅新旧两个激活项会打印日志

  const onMediaClicked = () => {
    if (!isMediaActive) {
      playerAPI.playMedia(video.id).catch(snackBarHandler(store));
      return;
    }
    playerAPI.pauseMedia().catch(snackBarHandler(store));
  };

  // 剩余渲染逻辑保持不变
  let activeMediaProps = {};
  if (isMediaActive) {
    activeMediaProps = {
      autoFocus: true,
      sx: { backgroundColor: "rgba(246,250,254,1)" },
    };
  }
  return (
    <Draggable draggableId={video.id} index={index}>
      {/* 原有渲染逻辑 */}
    </Draggable>
  );
});

方案二:父组件传参配合React.memo优化

如果采用父组件传递isActive参数的写法,只需要补全可观察标记和缓存逻辑即可:

const Playlist = observer(() => {
  const store = useAppStore();
  const items = store.playlist;
  const activeId = store.player.videoId; // 提前读取可观察值,触发依赖收集

  return (
    <List>
      {items.map((item) => (
        <MediaItem 
          key={item.id} // React列表必须加唯一key
          video={item}
          isActive={item.id === activeId} 
        />
      ))}
    </List>
  );
});

// 用React.memo包裹,仅props变化时才重渲染组件
const MediaItem = React.memo(observer(({ video, isActive }) => {
  let activeMediaProps = {};
  if (isActive) {
    activeMediaProps = {
      sx: { backgroundColor: "rgba(246,250,254,1)" },
    };
  }
  return <ListItem {...activeMediaProps}> ... </ListItem>;
}));

两种方案都可以实现仅激活状态变化的两个列表项重渲染,其余项完全不更新的效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 02:06:01