useWatch

在响应式数据变化时执行副作用,不会触发组件重新渲染

API 参考

语法

const currentValue = useWatch(watchSource, callback, options?)

参数

  • watchSource: 要监听的 store 实例、计算函数或源数组
  • callback: 监听数据变化时执行的函数(可返回清理函数)
  • options (可选):
    • immediate: 立即使用当前值执行回调(默认:false)
    • once: 仅执行一次回调,然后停止监听(默认:false)
    • deep: 启用深度比较以处理嵌套对象(默认:false)

返回值

  • currentValue: 监听源的最新状态值

配置选项

立即执行

useWatch(
  () => userStore.value.preferences,
  (preferences) => {
    applyUserPreferences(preferences);
  },
  { immediate: true } // 构建组件时立即执行回调
);

只执行一次

useWatch(
  () => userStore.value.isLoggedIn,
  (isLoggedIn) => {
    if (isLoggedIn) {
      console.log("检测到首次登录!");
      showWelcomeMessage();
    }
  },
  { once: true } // 仅触发一次
);

深度比较

const configStore = createStore({
  api: {
    endpoints: { users: "/api/users", orders: "/api/orders" },
    timeout: 5000,
  },
});

useWatch(
  () => configStore.value.api,
  (apiConfig, prevApiConfig) => {
    console.log("API 配置已更改");
    reinitializeApiClient(apiConfig);
  },
  { deep: true } // 深度比较,以避免不必要的副作用执行
);

示例

监听 Store 变化

跟踪 store 变化用于日志、分析或外部 API 调用:

const counterStore = createStore({ count: 0 });

function Counter() {
  // 仅监听变化,不触发重新渲染
  useWatch(counterStore, (current, prev) => {
    console.log(`计数: ${prev.count} → ${current.count}`);

    // 跟踪分析,比如埋点
    analytics.track("counter_changed", {
      from: prev.count,
      to: current.count,
    });
  });

  return (
    <button onClick={() => counterStore.setState((s) => s.count++)}>
      增加
    </button>
  );
}

监听计算值

监控计算值的业务逻辑:

const userStore = createStore({
  firstName: "张",
  lastName: "三",
  age: 30,
});

function UserTracker() {
  // 监听全名变化
  useWatch(
    () => `${userStore.value.firstName}${userStore.value.lastName}`,
    (fullName, prevFullName) => {
      if (prevFullName) {
        console.log(`姓名更新: "${prevFullName}" → "${fullName}"`);
        api.updateUserProfile({ fullName });
      }
    }
  );

  // 监听年龄里程碑
  useWatch(
    () => userStore.value.age,
    (age, prevAge) => {
      if (age >= 18 && prevAge < 18) {
        console.log("用户已成年!");
        unlockAdultFeatures();
      }
    }
  );

  return <div>正在跟踪用户变化...</div>;
}

监听多个源

同时监听多个 store 的变化:

const userStore = createStore({ name: "张三", role: "user" });
const settingsStore = createStore({ theme: "light", language: "zh" });

function AppWatcher() {
  useWatch(
    () => [userStore.value, settingsStore.value] as const,
    ([user, settings], [prevUser, prevSettings]) => {
      // 基于角色的逻辑
      if (user.role !== prevUser?.role) {
        console.log(`角色变化: ${prevUser?.role} → ${user.role}`);
        redirectBasedOnRole(user.role);
      }

      // 主题持久化
      if (settings.theme !== prevSettings?.theme) {
        console.log(`主题: ${prevSettings?.theme} → ${settings.theme}`);
        document.body.className = settings.theme;
        localStorage.setItem("theme", settings.theme);
      }
    }
  );

  return <div>监控应用状态...</div>;
}

清理函数

常用于管理订阅、定时器和外部资源等场景:

function WebSocketManager() {
  useWatch(
    () => userStore.value.connectionId,
    (connectionId, prevConnectionId) => {
      if (!connectionId) return;

      console.log(`连接到: ${connectionId}`);
      const ws = new WebSocket(`ws://api.com/${connectionId}`);

      ws.onopen = () => console.log("已连接");
      ws.onmessage = (event) => handleMessage(event.data);
      ws.onerror = (error) => console.error("WebSocket 错误:", error);

      // 清理函数 - 在下次变化或卸载时自动调用
      return () => {
        console.log(`断开连接: ${connectionId}`);
        ws.close();
      };
    }
  );

  return <div>WebSocket 管理器已激活</div>;
}

最佳实践

仅用于副作用

// Good:外部副作用
useWatch(
  () => store.value.theme,
  (theme) => {
    document.body.className = theme;
    localStorage.setItem("theme", theme);
  }
);

不要用于组件状态

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

明确监听指定属性变化

// Good:监听特定属性
useWatch(
  () => userStore.value.email,
  (email) => {
    validateEmail(email);
  }
);

避免监听所有状态

// Bad:任何 store 变化都会触发
useWatch(
  () => largeStore.value,
  (store) => {
    console.log("某些内容变化了"); // 太宽泛了
  }
);

始终清理资源

// Good:正确清理
useWatch(
  () => store.value.intervalMs,
  (ms) => {
    const interval = setInterval(doSomething, ms);
    return () => clearInterval(interval);
  }
);

Hook 比较

Hook用途重新渲染自动跟踪最适用于
useWatch监听状态变化并执行副作用❌ 否❌ 否特定副作用
useWatchEffect自动追踪依赖的响应式副作用❌ 否✅ 是一般副作用
useStoreValue同步 store 最新状态✅ 是❌ 否UI 状态
useComputed响应式更新的计算值✅ 是✅ 是计算数据

相关 Hook