Store 观察与反应(速查)
这是一份"5 分钟能用起来"的速查卡片:只需两段关键模板代码,你就能让自己的组件自动响应 store 的变化,或者在数据变化时触发自定义副作用(自动保存、埋点、日志……)。
📚 想深入了解 MobX 追踪规则、所有内置事件、性能优化建议,请看 响应式与事件。本页只做最常见的两件事。
把组件变成"observer":自动响应 store 变化
store 是一个响应式对象。要让 React 组件跟着 store 变化自动刷新,用 mobx-react-lite 的 observer 包一下:
import { observer } from 'mobx-react-lite';
import { createStore } from '@ydesign/react-editor';
const store = createStore({ key: 'YOUR_API_KEY' });
// 每当 store.objects 变化(新增 / 删除元素),这个组件会自动更新
const ElementsCount = observer(({ store }) => (
<div>当前画布共 {store.objects.length} 个元素</div>
));
核心规则:
- ✅ 组件里 读了哪个字段,就订阅哪个字段(MobX 按字段追踪)
- ✅ 只有读到的字段变化时才重新渲染 —— 性能天然友好
- ❌ 不要忘记
observer,否则组件不会更新
要观察当前选中元素?也只需要读 store.selectedElements:
const Hint = observer(({ store }) => {
const el = store.selectedElements[0];
if (!el) return <div>请先选中一个元素</div>;
return (
<div>
已选中 {el.type} · 位置 ({Math.round(el.left)}, {Math.round(el.top)})
</div>
);
});
Ydesign 所有内置面板、工具栏都在内部用了 observer,所以你一般只需要给自己写的组件加这个包裹。
监听 store 变化:store.on('change') 与 reaction
想不渲染 UI,只在数据变化时执行副作用(自动保存、埋点、同步后台……)?有两种方式,按场景选。
方式 A:最省心 —— store.on('change')
针对"画布对象"整体变化(新增 / 删除 / 修改)的高层订阅,底层做了深度对比,只在真实变化时触发:
const dispose = store.on('change', (objects) => {
console.log('画布变动:当前共', objects.length, '个元素');
// 典型用法:自动保存
fetch('/api/designs/current', {
method: 'PUT',
body: JSON.stringify(store.toJSON()),
});
});
// 不再需要时取消订阅
dispose();
适用场景:
- 自动保存
- 脏标记 / 未保存提示
- 简单的"有变化就做点什么"
方式 B:任意字段 —— mobx.reaction
只想订阅 某一个具体字段 的变化?用 MobX 的 reaction:
import { reaction } from 'mobx';
// 画布尺寸变化时,打印新的尺寸
const dispose = reaction(
() => [store.width, store.height], // 1) 数据源
([width, height]) => { // 2) 副作用
console.log('画布尺寸变为', width, '×', height);
},
);
dispose(); // 不再需要时
reaction 的第一个参数是一个纯读取函数(pure selector),第二个参数在它的返回值变化时被调用。它比 store.on('change') 更精细 —— 不相关的字段变化不会打扰到你。
💡 这两种方式可以同时使用。
store.on('change')负责"画布整体有变",reaction负责"某个具体字段有变"。
在 React 里正确管理订阅
上面两种订阅都返回一个 dispose 函数。在 React 里必须把它放到 useEffect 里,组件卸载时自动取消,否则会造成泄漏和内存增长:
import { useEffect } from 'react';
import { reaction } from 'mobx';
import { observer } from 'mobx-react-lite';
const AutoSave = observer(({ store }) => {
useEffect(() => {
// 1 秒节流的自动保存
const dispose = reaction(
() => store.toJSON(),
(json) => fetch('/api/save', { method: 'PUT', body: JSON.stringify(json) }),
{ delay: 1000 },
);
return dispose; // 👈 组件卸载时自动取消订阅
}, [store]);
return null;
});
同样的道理也适用于 store.on('change', ...):
useEffect(() => {
const dispose = store.on('change', (objects) => {
setDirty(true);
});
return dispose;
}, [store]);
Ydesign 避坑清单
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 组件不更新 | 忘了 observer 包裹 | 在 export default observer(MyComp) |
reaction 每秒触发几十次、请求被冲垮 | 拖拽 / 输入时 store.objects 高频变化 | 加 { delay: 1000 } 节流 |
| 组件卸载后还在打 log | dispose 没被调用 | 放进 useEffect 的 return |
| 改了字段组件却没反应 | 读取放在 useMemo / useCallback 里 | 把字段读取直接写在渲染函数体里 |
store.editor 偶尔是 null | Editor 是 <Workspace /> 挂载时才创建 | 用 store.editor?.xxx 可选链 |
再深入?
- 👉 响应式与事件 —— 完整的事件列表、
autorun、MobX + 事件混用、性能优化 - 👉 Store 总览 —— 所有响应式字段一览
- 👉 MobX 官方文档 / mobx-react-lite