meteor-react

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React interfaces for Meteor 3

Meteor 3 的 React 界面

React owns component rendering and local UI state. Meteor owns startup, Tracker reactivity, Minimongo, subscriptions, methods, and the build handoff. Keep those boundaries visible: components consume authorized client data and invoke methods, while publications and methods remain the server authority.
React 负责组件渲染和本地UI状态管理。Meteor 负责启动、Tracker响应式、Minimongo、订阅、方法以及构建交接。请明确这些职责边界:组件负责使用已授权的客户端数据并调用方法,而发布和方法则由服务器管控。

Decision flow

决策流程

  1. For a new JavaScript app, run
    meteor create --react <name>
    ; React is also the default skeleton. For TypeScript and TSX, run
    meteor create --typescript <name>
    .
  2. Inspect
    package.json
    ,
    .meteor/packages
    ,
    .meteor/versions
    , the client entry, and
    rspack.config.*
    . Skeletons and
    react-meteor-data
    versions can change independently of this skill.
  3. Mount one React root from
    Meteor.startup
    with
    createRoot
    .
  4. Pick one
    react-meteor-data
    surface for a component:
    • Classic hooks for explicit loading UI and synchronous Minimongo reads.
    • Suspense hooks only when an upstream Suspense fallback and rejection boundary already own the pending and error states.
  5. Use
    useSubscribe
    for readiness,
    useFind
    for reactive lists with stable document references, and
    useTracker
    for other Tracker values.
  6. Keep React-specific Rspack rules here. Route aliases, loaders, code splitting, cache, output, or migration work to the build skills.
  7. Test UI behavior in a browser-backed client suite and always unmount the rendered root so Tracker computations and subscriptions can stop.
  1. 对于新的JavaScript应用,执行
    meteor create --react <name>
    ;React 也是默认的骨架模板。对于TypeScript和TSX项目,执行
    meteor create --typescript <name>
  2. 检查
    package.json
    .meteor/packages
    .meteor/versions
    、客户端入口文件以及
    rspack.config.*
    。骨架模板和
    react-meteor-data
    的版本可能独立于本技能更新。
  3. Meteor.startup
    中使用
    createRoot
    挂载一个React根实例。
  4. 为组件选择一种
    react-meteor-data
    的使用方式:
    • 传统钩子:适用于显式加载UI和同步Minimongo读取场景。
    • Suspense钩子:仅当上游已存在Suspense回退组件和错误边界来处理等待和错误状态时使用。
  5. 使用
    useSubscribe
    处理订阅就绪状态,
    useFind
    获取具有稳定文档引用的响应式列表,
    useTracker
    处理其他Tracker值。
  6. React专属的Rspack规则请在此处理。路径别名、加载器、代码分割、缓存、输出或迁移相关工作请转至构建技能。
  7. 在浏览器端的客户端测试套件中测试UI行为,并且始终卸载渲染的根实例,以便Tracker计算和订阅可以停止。

Current scaffold

当前脚手架

bash
meteor create --react my-app
cd my-app
meteor
Current Meteor 3.4+ scaffolds use Rspack and
react-meteor-data
. Inspect
.meteor/versions
: Meteor 3 requires 3.0.0+, these examples target 4.0.1, and
references/react-meteor-data.md
records feature floors.
jsx
import { Meteor } from "meteor/meteor";
import { createRoot } from "react-dom/client";
import { App } from "/imports/ui/App";
import "/imports/ui/styles.css";

Meteor.startup(() => {
  const container = document.getElementById("react-target");
  if (!container) throw new Error("Missing #react-target");
  createRoot(container).render(<App />);
});
TypeScript uses
client/main.tsx
, a
tsconfig.json
, and usually
rspack.config.ts
. Meteor discovers that TypeScript config directly.
bash
meteor create --react my-app
cd my-app
meteor
当前Meteor 3.4+版本的脚手架使用Rspack和
react-meteor-data
。检查
.meteor/versions
:Meteor 3要求版本3.0.0+,以下示例针对4.0.1版本,
references/react-meteor-data.md
记录了功能最低版本要求。
jsx
import { Meteor } from "meteor/meteor";
import { createRoot } from "react-dom/client";
import { App } from "/imports/ui/App";
import "/imports/ui/styles.css";

Meteor.startup(() => {
  const container = document.getElementById("react-target");
  if (!container) throw new Error("Missing #react-target");
  createRoot(container).render(<App />);
});
TypeScript项目使用
client/main.tsx
tsconfig.json
,通常还会有
rspack.config.ts
。Meteor会自动识别TypeScript配置。

Reactive data selection

响应式数据选择

NeedAPIContract
Subscription readinessClassic
useSubscribe
Returns an
isLoading
function. Call it.
Reactive listClassic
useFind
Return a cursor, never
fetch()
.
User, count, single document, ReactiveVarClassic
useTracker
Return the reactive value. Avoid arbitrary side effects.
Async value with SuspenseSuspense
useTracker
Supply a stable key and Promise-producing function.
SSR-capable collection querySuspense
useFind
Pass the collection and the
find
argument tuple.
Existing class component
withTracker
Keep it during focused maintenance; plan hooks separately.
Classic list scaffold:
jsx
import { useFind, useSubscribe } from "meteor/react-meteor-data";
import { Tasks } from "/imports/api/tasks";

export function TaskList({ listId }) {
  const isLoading = useSubscribe("tasks.byList", listId);
  const tasks = useFind(
    () => Tasks.find({ listId }, { sort: { createdAt: -1 } }),
    [listId],
  );

  if (isLoading()) return <p>Loading...</p>;
  if (tasks.length === 0) return <p>No tasks.</p>;
  return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}
useFind
owns cursor observation and updates only changed document references. Use
useTracker
when a transformed aggregate is clearer. Read
references/react-meteor-data.md
before tuning dependencies or
skipUpdate
.
需求API约定
订阅就绪状态传统
useSubscribe
返回一个
isLoading
函数,必须调用该函数。
响应式列表传统
useFind
返回游标,绝不能返回
fetch()
的结果。
用户信息、计数、单个文档、ReactiveVar传统
useTracker
返回响应式值,避免任意副作用。
支持Suspense的异步值Suspense
useTracker
提供稳定的键和返回Promise的函数。
支持SSR的集合查询Suspense
useFind
传入集合和
find
参数元组。
现有类组件
withTracker
在重点维护期间保留该方式;单独规划钩子迁移。
传统列表脚手架示例:
jsx
import { useFind, useSubscribe } from "meteor/react-meteor-data";
import { Tasks } from "/imports/api/tasks";

export function TaskList({ listId }) {
  const isLoading = useSubscribe("tasks.byList", listId);
  const tasks = useFind(
    () => Tasks.find({ listId }, { sort: { createdAt: -1 } }),
    [listId],
  );

  if (isLoading()) return <p>Loading...</p>;
  if (tasks.length === 0) return <p>No tasks.</p>;
  return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}
useFind
负责游标观察,仅更新已变化的文档引用。当需要转换聚合数据时使用
useTracker
会更清晰。在调整依赖项或
skipUpdate
之前,请阅读
references/react-meteor-data.md

Suspense and async reactivity

Suspense与异步响应式

Suspense imports change signatures; they are not drop-in replacements:
jsx
import { Suspense } from "react";
import { useFind, useSubscribe } from "meteor/react-meteor-data/suspense";
import { Tasks } from "/imports/api/tasks";

function TaskList({ listId }) {
  useSubscribe("tasks.byList", listId);
  const tasks = useFind(Tasks, [
    { listId },
    { sort: { createdAt: -1 } },
  ], [listId]);
  return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}

export function TaskScreen({ listId }) {
  return (
    <AppErrorBoundary>
      <Suspense fallback={<p>Loading...</p>}>
        <TaskList listId={listId} />
      </Suspense>
    </AppErrorBoundary>
  );
}
Treat Suspense
useSubscribe
as a suspension-only call. In the currently audited package, its declared return type and runtime value disagree, so do not consume the return. Read
references/suspense-and-async.md
for stable keys, rejections, SSR, and
Tracker.withComputation
after
await
.
Suspense导入会改变签名,它们不能直接替换传统方式:
jsx
import { Suspense } from "react";
import { useFind, useSubscribe } from "meteor/react-meteor-data/suspense";
import { Tasks } from "/imports/api/tasks";

function TaskList({ listId }) {
  useSubscribe("tasks.byList", listId);
  const tasks = useFind(Tasks, [
    { listId },
    { sort: { createdAt: -1 } },
  ], [listId]);
  return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}

export function TaskScreen({ listId }) {
  return (
    <AppErrorBoundary>
      <Suspense fallback={<p>Loading...</p>}>
        <TaskList listId={listId} />
      </Suspense>
    </AppErrorBoundary>
  );
}
将Suspense版
useSubscribe
仅视为触发挂起的调用。在当前已审核的包中,其声明的返回类型与运行时值不一致,因此不要使用返回值。如需了解稳定键、错误处理、SSR以及
await
后的
Tracker.withComputation
,请阅读
references/suspense-and-async.md

Build and refresh ownership

构建与刷新管控

Rspack detects React from the app's npm dependencies, enables JSX or TSX SWC parsing, and installs and injects React Refresh for client development. A normal React Rspack app does not add the refresh plugin manually.
Keep
rspack.config.*
minimal. The generated JavaScript skeleton demonstrates an optional SVGR rule; the TypeScript skeleton demonstrates type checking. Compose custom rules through
defineConfig
from
@meteorjs/rspack
without replacing Meteor's SWC defaults.
RequestOwner
React detection, JSX or TSX, React Refresh boundaryThis skill
General
rspack.config
helpers, SWC, aliases, cache, chunks
meteor-modern-build-stack
Convert an existing app or legacy build plugin to Rspack
migrate-to-rspack
Meteor-bundler HMR or custom
module.hot
lifecycle
Build skill, then this skill for React symptoms
If edits reload the page or reset component state, first determine which bundler compiled the module. In a Rspack client graph, inspect React Refresh boundaries and avoid a second
react-fast-refresh
stack. In a Meteor-bundler graph, verify
hot-module-replacement
and the bundled React Fast Refresh integration. See
references/build-refresh-and-testing.md
.
Rspack会从应用的npm依赖中检测React,启用JSX或TSX的SWC解析,并安装和注入React Refresh用于客户端开发。普通的React Rspack应用无需手动添加刷新插件。
保持
rspack.config.*
尽可能简洁。生成的JavaScript骨架演示了可选的SVGR规则;TypeScript骨架演示了类型检查。通过
@meteorjs/rspack
defineConfig
组合自定义规则,无需替换Meteor的SWC默认配置。
请求内容负责技能
React检测、JSX/TSX、React Refresh边界本技能
通用
rspack.config
辅助、SWC、别名、缓存、代码块
meteor-modern-build-stack
将现有应用或旧版构建插件转换为Rspack
migrate-to-rspack
Meteor打包器的HMR或自定义
module.hot
生命周期
构建技能,之后针对React相关症状使用本技能
如果编辑后页面重新加载或组件状态重置,请先确定哪个打包器编译了该模块。在Rspack客户端依赖图中,检查React Refresh边界,避免出现第二个
react-fast-refresh
栈。在Meteor打包器依赖图中,验证
hot-module-replacement
和已打包的React Fast Refresh集成。详情请见
references/build-refresh-and-testing.md

Mutations and tests

数据变更与测试

Call
Meteor.callAsync
from event handlers, model pending and rejection in React state, and leave validation and authorization inside the method. Do not write sensitive collections directly from a component.
For tests, separate a presentational component that accepts data as props from the hook-owning container. Unit-test the former with the project's existing React test library. Test the latter in a browser client with controlled Minimongo or a real publication. Assert rendered behavior, not the number of reactive-function calls, because React can discard work and invoke initial render logic more than once.
从事件处理程序中调用
Meteor.callAsync
,在React状态中处理等待和错误状态,将验证和授权逻辑留在方法内部。不要从组件直接写入敏感集合。
对于测试,将接受数据作为props的展示组件与拥有钩子的容器组件分离。使用项目现有的React测试库对展示组件进行单元测试。在浏览器客户端中,使用受控的Minimongo或真实发布对容器组件进行测试。断言渲染行为,而不是响应式函数的调用次数,因为React可能会丢弃工作并多次调用初始渲染逻辑。

Anti-patterns

反模式

  • Treat classic
    isLoading
    as a boolean. It is a function and must be called.
  • Pass
    Tasks.find().fetch()
    to
    useFind
    . Return the cursor.
  • Put a subscription inside a
    useFind
    factory. Subscribe separately.
  • Call React state setters, methods, analytics, or arbitrary effects inside a
    useTracker
    reactive function.
  • Force every classic component onto Suspense. Both surfaces remain useful.
  • Reuse a generic Suspense key for unrelated mounted computations.
  • Read a Tracker source only after
    await
    without restoring the computation.
  • Add
    @rspack/plugin-react-refresh
    to project config when Meteor already injected it.
  • Replace all Rspack SWC options to add one React transform. Extend them.
  • Mock away all Meteor data behavior in a component integration test.
  • 将传统版
    isLoading
    当作布尔值处理。它是一个函数,必须调用。
  • useFind
    传入
    Tasks.find().fetch()
    。应返回游标。
  • useFind
    的工厂函数内部添加订阅。应单独订阅。
  • useTracker
    的响应式函数内部调用React状态设置器、方法、分析工具或任意副作用。
  • 强制所有传统组件切换到Suspense。两种方式都有各自的用途。
  • 为无关的挂载计算重用通用的Suspense键。
  • await
    后读取Tracker数据源但未恢复计算。
  • 当Meteor已自动注入时,仍向项目配置中添加
    @rspack/plugin-react-refresh
  • 为添加一个React转换而替换所有Rspack SWC选项。应扩展现有选项。
  • 在组件集成测试中模拟所有Meteor数据行为。

Authoritative resources

权威资源