跳转到内容

Effect

Effect 把“值变化 -> 执行业务副作用 -> 清理资源”收敛到统一行为中。

  • 本质:对订阅值变化的副作用包装。
  • 价值:副作用逻辑集中、清理时机可控、可与其他行为组合。
  • 特点:支持错误处理与初始化执行策略。
  • 需要把请求、埋点、日志、定时器等副作用与状态变化绑定。
  • 你希望在重跑或销毁时自动清理资源。
  • 你希望副作用逻辑脱离组件,便于复用和测试。
import { io } from '@iostore/store';
import { effect, withBehaviors } from '@iostore/store/behavior';
const count = io(0);
const view = withBehaviors(count, [
effect((value, previous) => {
console.log('effect run', { value, previous });
}),
]);

你可以递增计数并销毁视图,观察 effect 执行与 cleanup 次数。

当前值:0
effect 执行次数:0
cleanup 执行次数:0
import { useState } from 'react';
import { useIO } from '@iostore/react';
import { io } from '@iostore/store';
import { effect, withBehaviors } from '@iostore/store/behavior';
const store = io(0);
const metrics = io({ runs: 0, cleanups: 0 });
const view = withBehaviors(store, [
effect(
() => {
metrics.runs.set((n) => n + 1);
return () => metrics.cleanups.set((n) => n + 1);
},
{ immediate: false },
),
]);
function EffectLiveDemo() {
const [destroyed, setDestroyed] = useState(false);
const value = useIO(view);
const stats = useIO(metrics);
return (
<section className="io-live">
<div>当前值:{value}</div>
<div>effect 执行次数:{stats.runs}</div>
<div>cleanup 执行次数:{stats.cleanups}</div>
<div className="io-live__row">
<button
type="button"
className="io-live__button"
onClick={() => store.set((n) => n + 1)}
disabled={destroyed}
>
count +1
</button>
<button
type="button"
className="io-live__button"
disabled={destroyed}
onClick={() => {
view.destroy();
setDestroyed(true);
}}
>
destroy view
</button>
</div>
</section>
);
}
export default EffectLiveDemo;

观察点:每次值变化会触发新一轮 effect,并在重跑前或销毁时调用 cleanup。

effect((value) => {
const timer = setInterval(() => {
console.log('tick', value);
}, 1000);
return () => clearInterval(timer);
});

当值再次变化或 view.destroy() 执行时,清理函数会被调用。

effect(handler, {
immediate: true,
onError: (error) => console.error(error),
});
  • immediate:挂载后是否立即执行一次(默认 true)。
  • onError:effect/cleanup 抛错时的错误处理。
import { throttle, effect, withBehaviors } from '@iostore/store/behavior';
const view = withBehaviors(count, [
throttle(50),
effect((value) => {
sendMetric(value);
}),
]);
  • 在 effect 里直接写回同一状态且无保护:容易形成循环触发。
  • 忘记返回 cleanup:可能遗留计时器或监听器。
  • 在高频场景直接 effect 请求:应先节流或防抖。
  • 把副作用放在行为层,组件只负责展示与交互。
  • 高频输入链路优先 throttle/debounce -> effect
  • 对关键副作用配置 onError,避免静默失败。