---
name: rab-react
description: React响应式状态管理库，用于 `@rabjs/react` 包的 React 响应式开发指导。当用户提到 `@rabjs/react`、响应式组件、`RSRoot`、`RSStrict`、`observer`、`view`、`useService`、`useObserver`、`bindServices`、Service 注入、可观察状态管理或相关页面改造时，应优先使用这个 skill。覆盖 `@rabjs/react` 的核心模式、包内导出的组件名称 `RSRoot` 与 `RSStrict`，以及 observer/view 模式、依赖注入、Service 生命周期、异步状态追踪、事件系统和领域架构。
---

# @rabjs/react 快速参考

这是为大模型优化的 `@rabjs/react` (RSJS) 快速参考指南，聚焦最核心的使用模式。

## 🎯 五大核心概念

### 1. 响应式组件：`observer` 和 `view`

**基本规则**：组件必须使用 `observer` 或 `view` 包裹才能自动响应状态变化。

```typescript·
import { observer, view } from "@rabjs/react";

// ✅ 方式 1：observer（推荐用于函数组件）
const Counter = observer(() => {
  const service = useService(CounterService);
  return <div>{service.count}</div>; // 自动追踪 count 变化
});

// ✅ 方式 2：view（支持类组件和函数组件）
class Counter extends React.Component {
  render() {
    const { store } = this.props;
    return <div>{store.count}</div>;
  }
}
export default view(Counter);

// ❌ 错误：忘记使用 observer
const Counter = () => {
  const service = useService(CounterService);
  return <div>{service.count}</div>; // 不会自动更新！
};
```

**关键点**：

- 必须在 `observer` 内部访问 observable 属性
- 不要解构 observable 对象（会破坏响应性）
- `view` 和 `observer` 功能相同，`view` 额外支持类组件

### 2. Service：业务逻辑容器

**基本规则**：所有业务逻辑都应该封装在 Service 类中，包括组件内的操作方法。

```typescript
import { Service } from "@rabjs/react";

export class CounterService extends Service {
  // 属性自动是 observable
  count = 0;

  // 方法自动是 action（批量更新）
  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }

  // 计算属性（getter）
  get doubleCount() {
    return this.count * 2;
  }

  // 异步方法自动追踪 loading 和 error
  async fetchData() {
    const response = await fetch("/api/data");
    this.count = await response.json();
  }
}

// 在组件中访问异步状态
const Component = observer(() => {
  const service = useService(CounterService);

  // 自动生成的状态
  if (service.$model.fetchData.loading) return <div>加载中...</div>;
  if (service.$model.fetchData.error) return <div>错误</div>;
  // 可以直接使用，框架会处理好 this指向问题
  return <div onClick={service.fetchData}>{service.count}</div>;
});
```

**关键点**：

- Service 类继承自 `Service` 基类
- 所有属性自动是响应式的（observable）
- 所有方法默认就是 action（批量更新），**不需要也不应该再写 `@Action` 装饰器**
- 只有需要**关闭**批量更新时才用 `@SyncAction` 标记方法
- 异步方法自动追踪 `loading` 和 `error` 状态（通过 `$model.methodName`）

### 3. `useService` + `bindServices`：连接组件和 Service

**基本规则**：使用 `bindServices` 注册 Service，使用 `useService` 获取 Service 实例。

```typescript
import { observer, useService, bindServices } from "@rabjs/react";

// 1. 定义组件内容
const CounterContent = observer(() => {
  const service = useService(CounterService);
  return (
    <div>
      <p>{service.count}</p>
      <button onClick={() => service.increment()}>+1</button>
    </div>
  );
});

// 2. 使用 bindServices 注册服务并导出
export default bindServices(CounterContent, [CounterService]);

// 注册多个服务
export default bindServices(PageContent, [
  AuthService,
  UserService,
  DataService,
]);
```

**关键点**：

- `bindServices` 创建一个容器（领域）并注册 Service
- `useService` useService 使用前 对应的 Service 必须已经经过 bindService 或者 register 注册过
- `bindServices` 会自动将组件包裹为 `observer`（如果还没有的话）
- 容器与组件生命周期绑定：组件挂载时创建，卸载时销毁，容器内绑定的 Service 也跟随容器一起创建和销毁
- Service可见性：子组件可以访问父组件及组件树上一直到全局的 Service，比如页面bingServices 后，页面下子组件直接就可以useService

### 4. 组件和 Service 生命周期

**核心原则**：

- **全局单例 Service** → 使用 `register` 注册，**禁止**使用 `bindServices`
- **组件生命周期绑定的 Service** → 使用 `bindServices` 注册

#### 全局单例 Service（应用级）

```typescript
import { register, resolve, Service } from '@rabjs/react';

// 定义全局 Service
export class AppService extends Service {
  appName = 'My App';
  theme = 'light';
}

export class LoggerService extends Service {
  log(message: string) {
    console.log(`[Log] ${message}`);
  }
}

// ✅ 正确：在应用启动时使用 register 注册全局 Service
register(AppService);
register(LoggerService);

// 在任何地方解析全局 Service
const appService = resolve(AppService);
const loggerService = resolve(LoggerService);

// ❌ 错误：全局 Service 禁止使用 bindServices
// export default bindServices(App, [AppService]); // 错误！
```

#### 组件生命周期绑定的 Service

```typescript
// ✅ 正确：使用 bindServices 绑定到组件生命周期
export class PageService extends Service {
  pageData: any[] = [];

  async loadData() {
    // 页面级数据加载
  }
}

const PageContent = observer(() => {
  const service = useService(PageService);

  useEffect(() => {
    service.loadData();
  }, [service]);

  return <div>{service.pageData.length}</div>;
});

// 使用 bindServices 注册
export default bindServices(PageContent, [PageService]);

// 当组件挂载时：容器创建，Service 实例化
// 当组件卸载时：容器销毁，Service 清理
```

**生命周期对比**：

| 类型         | 注册方式         | 生命周期 | 创建时机 | 销毁时机 | 使用场景                       |
| ------------ | ---------------- | -------- | -------- | -------- | ------------------------------ |
| **全局单例** | `register()`     | 应用级   | 应用启动 | 应用关闭 | 全局配置、主题、用户认证等     |
| **组件绑定** | `bindServices()` | 组件级   | 组件挂载 | 组件卸载 | 页面数据、表单状态、局部功能等 |

### 5. Service 之间的关系

**核心原则**：Service 内部使用其他 Service 时，使用 getter + `resolve()` 获取依赖。

```typescript
import { Service, register } from '@rabjs/react';

// 依赖的 Service
export class LoggerService extends Service {
  log(message: string) {
    console.log(`[Log] ${message}`);
  }
}
// 全局Service
register(LoggerService);

export class ApiService extends Service {
  // ✅ 推荐写法：getter + this.resolve
  get loggerService() {
    return this.resolve(LoggerService);
  }

  async fetchUsers() {
    this.loggerService.log('Fetching users...');
    return fetch('/api/users').then(r => r.json());
  }
}
// 另一个全局Service
register(ApiService);

// 使用依赖的 Service
export class UserService extends Service {
  users: any[] = [];

  // ✅ 推荐写法：getter + this.resolve
  get loggerService() {
    return this.resolve(LoggerService);
  }

  get apiService() {
    return this.resolve(ApiService);
  }

  async loadUsers() {
    this.loggerService.log('Loading users...');
    this.users = await this.apiService.fetchUsers();
    this.loggerService.log('Users loaded');
  }
}

// 依赖（LoggerService、ApiService）已在全局注册，
// UserService 的容器会沿容器树向上解析到全局容器，因此这里只需注册 UserService
export default bindServices(UserPage, [UserService]);
```

**重要限制**：

- `this.resolve()` 从**当前实例所属的容器**开始解析，沿容器树向上查找，因此被依赖的 Service 必须注册在**同一棵容器树**里（当前容器、任一父级容器或全局容器）
- `resolve()` 可以解析：
  - ✅ 当前容器注册的 Service
  - ✅ 父组件容器注册的 Service
  - ✅ 全局注册的 Service（通过 `register`）
- `resolve()` 不能解析：
  - ❌ 兄弟组件容器的 Service
  - ❌ 子组件容器的 Service
  - ❌ 未注册的 Service

**作用域链示例**：

```typescript
// 全局 Service
register(GlobalService);
// 全局注册的单例Service可以直接通过 resolve 获取到实例
resolve(GlobalService);
// 应用级
const AppContent = observer(() => {
  return <Page />;
});
export const App = bindServices(AppContent, [AppService]);

// 页面级
const PageContent = observer(() => {
  return <Component />;
});
export const Page = bindServices(PageContent, [PageService]);

// 组件级
export class ComponentService extends Service {
  // ✅ 可以 resolve：
  get globalService() {
    return this.resolve(GlobalService); // 全局注册的
  }

  get appService() {
    return this.resolve(AppService); // 父级容器注册的
  }

  get pageService() {
    return this.resolve(PageService); // 父级容器注册的
  }

  // ❌ 不能 resolve：
  get siblingService() {
    return this.resolve(SiblingService); // 兄弟容器的 Service
  }
}

export default bindServices(ComponentContent, [ComponentService]);
```

**遗留写法（不推荐）：`@Inject` 装饰器**

`@Inject` 属性装饰器仍然可用（源码保留），但不再是推荐用法。新代码请统一使用上面的 getter + `this.resolve` 模式：

```typescript
import { Service, Inject } from '@rabjs/react';

export class UserService extends Service {
  // ⚠️ 遗留写法：仍可用，但不推荐。请改用 getter + this.resolve
  @Inject(LoggerService)
  private logger!: LoggerService;

  @Inject(ApiService)
  private api!: ApiService;

  async loadUsers() {
    this.logger.log('Loading users...');
    this.users = await this.api.fetchUsers();
  }
}
```

`@Inject` 的解析规则与 `this.resolve` 相同：被注入的 Service 必须已注册在当前实例所属的容器树中（当前容器、父级容器或全局容器），否则解析失败。

## 🔍 快速诊断

### 组件不更新？

1. **检查是否使用了 `observer` 或 `view`**

   ```typescript
   const Component = observer(() => { ... }); // ✅
   ```

2. **检查是否在 render 内部访问 observable**

   ```typescript
   // ✅ 在 render 内部
   const Component = observer(() => {
     return <div>{service.count}</div>;
   });
   ```

3. **检查是否解构了 observable**
   ```typescript
   // ❌ 避免解构
   const { count } = service;
   // ✅ 直接访问
   service.count;
   ```

### useService 报错？

1. **检查是否在 `bindServices` 内部使用**

   ```typescript
   export default bindServices(Content, [MyService]); // ✅
   ```

2. **检查 Service 是否已注册**
   ```typescript
   bindServices(Component, [MyService]); // ✅ 确保注册
   ```

### Service 依赖报错？

1. **检查依赖的 Service 是否已注册**

   ```typescript
   // ✅ 必须注册所有依赖
   bindServices(Component, [LoggerService, UserService]);
   ```

2. **检查作用域链**
   ```typescript
   // ❌ 不能访问兄弟组件的 Service
   // ✅ 只能访问父级或全局的 Service
   ```

## 📁 项目结构最佳实践

推荐的目录结构，清晰展示分层架构和 Services 与 Components 的绑定关系：

```
src/
├── services/                    # 全局 Services（使用 register 注册）
│   ├── logger.service.ts        # 日志服务（全局单例）
│   ├── api.service.ts           # API 服务（全局单例）
│   ├── auth.service.ts          # 认证服务（全局单例）
│   └── theme.service.ts         # 主题服务（全局单例）
│
├── pages/                       # 页面级组件（使用 bindServices）
│   ├── home/
│   │   ├── index.tsx            # 页面组件（bindServices + HomeService）
│   │   ├── home.service.ts      # 页面级 Service（页面生命周期）
│   │   └── components/          # 页面内的子组件
│   │       └── banner/
│   │           ├── index.tsx
│   │           └── banner.service.ts
│   │
│   ├── user/
│   │   ├── index.tsx            # 用户页面
│   │   ├── user.service.ts      # 用户页面 Service
│   │   └── components/
│   │       └── profile/
│   │           ├── index.tsx
│   │           └── profile.service.ts
│   │
│   └── todo/
│       ├── index.tsx            # Todo 页面
│       ├── todo.service.ts      # Todo 页面 Service
│       └── components/
│           ├── todo-list/
│           │   ├── index.tsx
│           │   └── todo-list.service.ts
│           └── todo-form/
│               ├── index.tsx
│               └── todo-form.service.ts
│
├── components/                  # 通用组件（使用 bindServices）
│   ├── search-bar/
│   │   ├── index.tsx
│   │   └── search-bar.service.ts  # 组件级 Service（组件生命周期）
│   │
│   └── data-table/
│       ├── index.tsx
│       └── data-table.service.ts  # 组件级 Service（组件生命周期）
│
├── app.tsx                      # 应用根组件
└── main.tsx                     # 应用入口（注册全局 Services）
```

### 代码示例

#### 1. 全局 Services 注册（main.tsx）

```typescript
// main.tsx
import { register } from "@rabjs/react";
import { LoggerService } from "./services/logger.service";
import { ApiService } from "./services/api.service";
import { AuthService } from "./services/auth.service";
import { ThemeService } from "./services/theme.service";

// ✅ 在应用启动时注册全局 Services
register(LoggerService);
register(ApiService);
register(AuthService);
register(ThemeService);

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
```

#### 2. 全局 Service 定义（services/logger.service.ts）

```typescript
// services/logger.service.ts
import { Service } from '@rabjs/react';

export class LoggerService extends Service {
  log(message: string) {
    console.log(`[Log] ${message}`);
  }

  error(message: string) {
    console.error(`[Error] ${message}`);
  }
}
```

#### 3. 页面级组件（pages/home/index.tsx）

```typescript
// pages/home/index.tsx
import { observer, useService, bindServices } from "@rabjs/react";
import { HomeService } from "./home.service";
import { UserInfo } from "@/components/user-info";

const HomeContent = observer(() => {
  const homeService = useService(HomeService);

  return (
    <div>
      <h1>{homeService.title}</h1>
      <UserInfo />
      <button onClick={() => homeService.loadData()}>加载数据</button>
    </div>
  );
});

// ✅ 使用 bindServices 注册页面级 Service
export default bindServices(HomeContent, [HomeService]);
```

#### 4. 页面级 Service（pages/home/home.service.ts）

```typescript
// pages/home/home.service.ts
import { Service } from '@rabjs/react';
import { LoggerService } from '@/services/logger.service';
import { ApiService } from '@/services/api.service';

export class HomeService extends Service {
  title = '首页';
  data: any[] = [];

  // ✅ 使用 getter + resolve 访问全局 Service
  get loggerService() {
    return this.resolve(LoggerService);
  }

  get apiService() {
    return this.resolve(ApiService);
  }

  async loadData() {
    this.loggerService.log('Loading home data...');
    this.data = await this.apiService.fetchHomeData();
    this.loggerService.log('Home data loaded');
  }
}
```

#### 5. 通用组件（components/user-info/index.tsx）

```typescript
// components/user-info/index.tsx
import { observer, useService, bindServices } from "@rabjs/react";
import { UserInfoService } from "./user-info.service";

const UserInfoContent = observer(() => {
  const service = useService(UserInfoService);

  return (
    <div>
      <p>用户名: {service.userName}</p>
      <p>邮箱: {service.email}</p>
    </div>
  );
});

// ✅ 使用 bindServices 注册组件级 Service
export const UserInfo = bindServices(UserInfoContent, [UserInfoService]);
```

#### 6. 组件级 Service（components/user-info/user-info.service.ts）

```typescript
// components/user-info/user-info.service.ts
import { Service } from '@rabjs/react';
import { AuthService } from '@/services/auth.service';

export class UserInfoService extends Service {
  userName = '';
  email = '';

  // ✅ 访问全局 AuthService
  get authService() {
    return this.resolve(AuthService);
  }

  constructor() {
    super();
    // 初始化时从 AuthService 获取用户信息
    const user = this.authService.currentUser;
    this.userName = user?.name || '';
    this.email = user?.email || '';
  }
}
```

### 架构层次关系

```
┌─────────────────────────────────────────────────────────┐
│  全局层（Global Layer）                                  │
│  - register() 注册                                       │
│  - 应用级生命周期                                        │
│  - LoggerService, ApiService, AuthService, ThemeService │
└─────────────────────────────────────────────────────────┘
                          ↓ 可访问
┌─────────────────────────────────────────────────────────┐
│  页面层（Page Layer）                                    │
│  - bindServices() 注册                                   │
│  - 页面级生命周期                                        │
│  - HomeService, UserService, TodoService                │
└─────────────────────────────────────────────────────────┘
                          ↓ 可访问
┌─────────────────────────────────────────────────────────┐
│  组件层（Component Layer）                               │
│  - bindServices() 注册                                   │
│  - 组件级生命周期                                        │
│  - UserInfoService, SearchBarService, DataTableService  │
└─────────────────────────────────────────────────────────┘
```

### 关键原则

1. **全局 Services**（services/）
   - ✅ 使用 `register()` 注册
   - ✅ 在 `main.tsx` 中注册
   - ✅ 应用级生命周期
   - ❌ 禁止使用 `bindServices`

2. **页面级 Services**（pages/*/）
   - ✅ 使用 `bindServices()` 注册
   - ✅ 页面组件生命周期
   - ✅ 可访问全局 Services
   - ✅ Service 文件与组件文件同目录

3. **组件级 Services**（components/*/）
   - ✅ 使用 `bindServices()` 注册
   - ✅ 组件生命周期
   - ✅ 可访问全局和父级 Services
   - ✅ Service 文件与组件文件同目录

4. **命名约定**
   - Service 文件：`xxx.service.ts`
   - 组件文件：`index.tsx`
   - 目录名：kebab-case（user-info, search-bar）

## 📖 进阶主题

更多高级功能和详细说明，请参考以下文档：

- **[异步操作和状态追踪](references/async-operations.md)** - `$model`、loading、error 状态详解
- **[计算属性和缓存](references/computed-properties.md)** - getter、`@Memo` 装饰器
- **[事件系统](references/event-system.md)** - 容器级事件、全局事件、`emit`/`on`/`off`
- **[装饰器](references/decorators.md)** - 默认 Action 语义、`@SyncAction`、`@Debounce`、`@Throttle`、`@Memo`、`@On`，以及遗留的 `@Action`/`@Inject`
- **[Hooks API](references/hooks-api.md)** - `useObserver`、`useLocalObservable`、`useReaction` 等
- **[领域架构](references/domain-architecture.md)** - 多级嵌套、作用域链、跨领域通信
- **[Observable API](references/observable-api.md)** - `observable`、`raw`、`observe`、`unobserve`
- **[SSR 支持](references/ssr.md)** - `enableStaticRendering`
- **[调试技巧](references/debugging.md)** - 常见问题和解决方案
- **[最佳实践](references/best-practices.md)** - 代码组织、性能优化

## 💡 核心模式总结

```typescript
// 1. 定义 Service
export class CounterService extends Service {
  count = 0;
  increment() {
    this.count++;
  }
}

// 2. 创建响应式组件
const CounterContent = observer(() => {
  const service = useService(CounterService);
  return (
    <div>
      <p>{service.count}</p>
      <button onClick={() => service.increment()}>+1</button>
    </div>
  );
});

// 3. 使用 bindServices 导出
export default bindServices(CounterContent, [CounterService]);
```

**记住这三步，就掌握了 RSJS 的核心用法！**
