useWatchEffect

自动跟踪的响应式副作用,立即运行并自动清理

为什么选择 useWatchEffect?

当你需要响应式副作用,但又不想手动指定依赖项时的完美选择。

可以将其视为"更智能的 useEffect",它知道自己依赖于哪些 store。

API 参考

语法

useWatchEffect(effect);

参数

  • effect: 包含副作用逻辑的函数(可返回清理函数)

返回值

  • undefined: 无返回值

示例

简单响应式副作用

const userStore = createStore({
  name: "张三",
  isOnline: false,
});

function DocumentTitleUpdater() {
  useWatchEffect(() => {
    // 自动跟踪 userStore 访问
    document.title = `${userStore.value.name} - ${
      userStore.value.isOnline ? "在线" : "离线"
    }`;
  });

  return <div>标题更新器已激活</div>;
}

无需依赖数组 - ZenBox 自动检测此副作用依赖于 userStore

自动保存与清理

const documentStore = createStore({
  content: "",
  autoSave: true,
  saveInterval: 30000,
});

function AutoSaveEffect() {
  useWatchEffect(() => {
    // 条件逻辑与提前返回
    if (!documentStore.value.autoSave) return;

    console.log(`设置自动保存,间隔 ${documentStore.value.saveInterval}ms`);

    const interval = setInterval(() => {
      console.log("自动保存内容...");
      api.saveDocument(documentStore.value.content);
    }, documentStore.value.saveInterval);

    // 清理函数在下次副作用运行前或卸载时自动执行
    return () => {
      console.log("清理自动保存间隔");
      clearInterval(interval);
    };
  });

  return <div>自动保存管理器已激活</div>;
}

智能依赖跟踪:当 autoSavesaveIntervalcontent 变化时,副作用会自动重新运行。

分析跟踪器

const analyticsStore = createStore({
  userId: null,
  sessionId: null,
  pageViews: 0,
  events: [],
});

function AnalyticsTracker() {
  useWatchEffect(() => {
    const { userId, sessionId, pageViews } = analyticsStore.value;

    if (!userId || !sessionId) return;

    // 跟踪页面访问计数
    if (pageViews > 0) {
      analytics.track("page_view_count", {
        userId,
        sessionId,
        count: pageViews,
        timestamp: Date.now(),
      });
    }

    // 设置用户标识
    analytics.identify(userId, {
      sessionId,
      lastActive: Date.now(),
    });

    console.log(
      `分析: 跟踪用户 ${userId}, 会话 ${sessionId}, ${pageViews} 次访问`
    );
  });

  return null;
}

useWatchEffect vs useWatch

特性useWatchEffectuseWatch
依赖跟踪✅ 自动❌ 手动
初始执行✅ 立即❌ 默认仅在变化时
配置✅ 零配置⚠️ 需要源参数
最适用于一般副作用特定数据监听

useWatchEffect - 当需要"响应式 useEffect"时:

// 自动依赖跟踪
useWatchEffect(() => {
  if (userStore.value.isLoggedIn) {
    initializeUserSession(userStore.value.id);
  }
});

useWatch - 当需要精确控制时:

// 手动依赖指定
useWatch(
  () => userStore.value.loginAttempts,
  (attempts) => {
    if (attempts > 3) {
      lockAccount();
    }
  }
);

最佳实践

保持副作用专注和纯净

// Good:单一职责
useWatchEffect(() => {
  document.title = `${appStore.value.title} - MyApp`;
});

使用提前返回处理条件逻辑

// Good:清晰的条件逻辑
useWatchEffect(() => {
  if (!settingsStore.value.enableNotifications) return;

  setupNotificationService();
  return () => teardownNotificationService();
});

始终为资源返回清理函数

// Good:正确的资源管理
useWatchEffect(() => {
  const timer = setInterval(updateClock, 1000);
  return () => clearInterval(timer);
});

不要在副作用内修改响应式状态

// Bad:创建无限循环
useWatchEffect(() => {
  const count = counterStore.value.count;
  counterStore.setState((state) => {
    state.count = count + 1; // 这会再次触发副作用,导致无限循环
  });
});

不要用于组件渲染状态

// Bad:不必要的间接状态
const [localState, setLocalState] = useState("");
useWatchEffect(() => {
  setLocalState(store.value.data); // 应该使用 useStoreValue 替代
});

Hook 比较

Hook依赖跟踪立即执行重新渲染使用场景
useWatchEffect✅ 自动✅ 是❌ 否一般响应式副作用
useWatch❌ 手动❌ 否❌ 否特定数据监听
useEffect❌ 手动依赖✅ 是❌ 否React 特定副作用
useStoreValue❌ 手动✅ 是✅ 是组件状态同步

相关 Hook