簡單回答就是 生命週期,但在脆上有批 React 仔永遠當它不存在的在賣課,確實也蠻擔心的,但你深入到一定的程度,就會發現那是一個賣課的話術。
經歷我們前面的實作,你應該也能深刻體會到生命週期的意思,這篇就來帶你釐清三個重點:
createEffect、signal.set),正確放在哪裡。TL;DR
- 在 React 裡,render 必須純(Function Component 的概念):不要在 render 期做副作用或寫入 signals。
- 副作用分工:
- UI 相關(DOM 量測、動畫)→
useLayoutEffect/useEffect- 資料流相關(依信號值觸發業務、請求)→ signals 的
createEffect(透過適配器管理生命週期)- 訂閱一律走
useSyncExternalStore(下一篇會寫),避免 tearing。

scheduler 在 microtask 時間點合併重跑 effect。createEffect;交給 hook 幫你管。useSyncExternalStore 做 快照 + 訂閱,React 能在 commit 前重取快照,防 tearing。createEffect
function Bad() {
  // render 期創建外部 effect,破壞純度與可預測性
  createEffect(() => {
    console.log("value", someSignal.get());
  });
  return <>{/**...你的UI */}</>;
}
signal.set
function Bad() {
  const v = someSignal.get();
  if (v < 0) someSignal.set(0); // render 期間寫入,會導致無窮重渲染
  return <>{/**...你的UI */}</>;
}
get() 值塞進閉包長期使用function Bad() {
  const v = someSignal.get();  // 這是當下快照
  const onClick = () => console.log(v); // 之後永遠印舊值
  return <button onClick={onClick}>log</button>;
}
useSyncExternalStore 包一個 useSignalValue(src)。
peek()(不建立 React→signal 依賴,但在 stale 時會 lazy 重算)。createEffect 監聽來源變動,並在清理時解除。useLayoutEffect / useEffect;把 signals 的值傳給 React,由 React 掌控 DOM 時機。這邊先不實作,給個概念讓大家先思考。
React 在事件處理內會自動批次 setState;我們的 batch 只影響 signals 的 effect 合併重跑,兩者互不衝突。
範例:
batch(() => {
    a.set(10)
    b.set(20)
    a.set(30)
});
// our effect 只有在 microtask 重跑一次
// React 如果有 setState 也會在事件內批次 render 一次
| 事情 | 放哪裡 | 說明 | 
|---|---|---|
| 讀寫 DOM、量測、動畫 | useLayoutEffect/useEffect | 由 React 的 Commit 管理時機 | 
| 根據信號值觸發業務副作用 | createEffect(透過 hook 訂閱) | 我們的 scheduler 會合併重跑 | 
| 多次更新只想重跑一次 | batch或transaction | 影響 signals 的 effect,不影響 React 的 commit | 
| 讓 React 訂閱外部狀態 | useSyncExternalStore | 避免 tearing,支援 Concurrent | 
| 測試或示範想立刻重跑 our effects | flushSync()(我們的) | 立刻清空 scheduler 佇列 | 
function Bad() {
  const [state, setState] = useState();
  const v = someSignal.get();
  useEffect(() => {
    // 這樣訂閱常會 tearing 或重複更新
    const stop = createEffect(() => {
      someSignal.get();
      setState(Date.now());
    });
    return () => stop();
  }, []);
  return <div>{v}</div>; // v 不是 React 管控的快照,要使用 state 做封包
}
useSyncExternalStore 取得快照function Good() {
  // 下一篇會實作這個hook
  const v = useSignalValue(someSignal);
  return <div>{v}</div>; // v 是 React 管控的同步快照,透過custom hook的方式整理
}
有了上述的概念,你應該對 React 的狀態管理和更新機制,有了比較正確的認知。
我們的目的不是要取代 React 的狀態,而是教你如何把 signals 當外部資料源,在 Concurrent 模式下不撕裂、少重跑、行為可預期。
下一篇,我們來實作能在 React 環境下正確使用前面建立的 signal 系統。