# 常用开发模式

API 集成、状态管理、页面导航的常用开发模式和最佳实践。

---

## 📋 目录

1. [API 集成模式](#api-集成模式)
2. [状态管理模式](#状态管理模式)
3. [页面导航模式](#页面导航模式)

---

## 🌐 API 集成模式

### 豆包智能服务的端能力 API 调用

使用豆包智能服务的端能力 API 访问系统能力时，先确定调用位置和状态反馈。这些 API 也称 Open API，代码统一从
`@doubao-dev/framework/api` 导入；具体参数和返回类型以 [豆包智能服务的端能力 API 目录](../doubao-agentic-service-api/groups.md) 为准。
`request` 本身不是泛型函数，不要写 `request<T>()`；如果需要业务返回类型，先接收 `response`，再对
`response.data` 做类型收窄或断言。

```tsx
import { useState } from '@doubao-dev/framework';
import { request } from '@doubao-dev/framework/api';

function getErrorMessage(error: unknown, fallback: string) {
  return error instanceof Error ? error.message : fallback;
}

function DataFetchExample() {
  const [data, setData] = useState<Record<string, unknown> | string | ArrayBuffer | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const fetchData = async () => {
    try {
      setLoading(true);
      setError('');

      const response = await request({
        url: 'https://api.example.com/data',
        method: 'GET',
        header: {
          'Content-Type': 'application/json'
        }
      });

      const responseData = response.data;
      setData(responseData ?? null);
    } catch (err) {
      setError(getErrorMessage(err, '获取数据失败'));
    } finally {
      setLoading(false);
    }
  };

  return (
    <view>
      <button onClick={fetchData}>获取数据</button>
      {loading && <text>加载中...</text>}
      {error && <text>错误: {error}</text>}
      {data && <text>数据: {JSON.stringify(data)}</text>}
    </view>
  );
}
```

### POST 请求示例

```tsx
const submitForm = async (formData: Record<string, unknown>) => {
  try {
    const response = await request({
      url: 'https://api.example.com/submit',
      method: 'POST',
      header: {
        'Content-Type': 'application/json'
      },
      data: formData
    });

    if (response.statusCode >= 200 && response.statusCode < 300) {
      console.log('提交成功');
    }
  } catch (err) {
    console.error('提交失败:', err);
  }
};
```

### 请求拦截和错误处理

```tsx
interface ApiError {
  code: number;
  message: string;
}

interface ApiRequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  header?: Record<string, string>;
  data?: Record<string, unknown> | string | ArrayBuffer;
  dataType?: 'json' | 'string';
}

function getResponseMessage(data: unknown) {
  if (typeof data === 'object' && data !== null && 'message' in data) {
    return String(data.message);
  }
  return '请求失败';
}

function isApiError(error: unknown): error is ApiError {
  return typeof error === 'object' && error !== null && 'code' in error && 'message' in error;
}

async function apiRequest<T>(url: string, options: ApiRequestOptions): Promise<T> {
  try {
    const response = await request({
      url,
      ...options
    });

    // 检查响应状态
    if (response.statusCode >= 400) {
      throw {
        code: response.statusCode,
        message: getResponseMessage(response.data)
      } as ApiError;
    }

    return response.data as T;
  } catch (err) {
    // 统一错误处理
    if (isApiError(err) && err.code === 401) {
      console.error('未授权，请登录');
    } else if (isApiError(err) && err.code === 404) {
      console.error('资源不存在');
    } else {
      console.error('请求错误:', getErrorMessage(err, '请求失败'));
    }
    throw err;
  }
}

// 使用示例
async function fetchUserData(userId: string) {
  try {
    const data = await apiRequest<User>(`https://api.example.com/users/${userId}`, {
      method: 'GET'
    });
    return data;
  } catch (err) {
    // 业务逻辑错误处理
    return null;
  }
}
```

### 豆包智能服务的端能力 API 使用配方

`common-patterns.md` 只保留通用调用结构。`getLocation`、Storage 等具体端能力的组合示例见
[doubao-agentic-service-api-recipes.md](./doubao-agentic-service-api-recipes.md)；完整参数和返回类型见 [豆包智能服务的端能力 API 目录](../doubao-agentic-service-api/groups.md)。

## 📊 状态管理模式

### 1. 本地状态 (useState)

适用于组件内部状态管理。

```tsx
import { useState } from '@doubao-dev/framework';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <view>
      <text>计数: {count}</text>
      <button onClick={() => setCount(count + 1)}>增加</button>
      <button onClick={() => setCount(count - 1)}>减少</button>
      <button onClick={() => setCount(0)}>重置</button>
    </view>
  );
}
```

### 2. 副作用管理 (useEffect)

适用于数据获取、订阅、定时器等副作用。

```tsx
import { useState, useEffect } from '@doubao-dev/framework';
import { request } from '@doubao-dev/framework/api';

function DataLoader() {
  const [data, setData] = useState<Record<string, unknown> | string | ArrayBuffer | null>(null);
  const [error, setError] = useState('');

  useEffect(() => {
    // 数据获取
    const fetchData = async () => {
      try {
        setError('');
        const response = await request({
          url: 'https://api.example.com/data',
          method: 'GET'
        });
        setData(response.data ?? null);
      } catch (error) {
        setError(error instanceof Error ? error.message : '获取数据失败');
      }
    };

    void fetchData();

    // 清理函数
    return () => {
      console.log('组件卸载，清理资源');
    };
  }, []); // 空依赖数组表示只在挂载时执行一次

  if (error) {
    return <view>{error}</view>;
  }

  return <view>{data ? <text>{JSON.stringify(data)}</text> : <text>加载中...</text>}</view>;
}
```

### 3. 复杂状态 (useReducer)

适用于复杂状态逻辑。

```tsx
import { useReducer } from '@doubao-dev/framework';

// 定义状态和动作类型
interface State {
  count: number;
  step: number;
}

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'setStep'; step: number }
  | { type: 'reset' };

// Reducer 函数
function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + state.step };
    case 'decrement':
      return { ...state, count: state.count - state.step };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      return state;
  }
}

function ComplexCounter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });

  return (
    <view>
      <text>计数: {state.count}</text>
      <text>步长: {state.step}</text>
      <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
      <button onClick={() => dispatch({ type: 'setStep', step: 5 })}>
        设置步长为 5
      </button>
      <button onClick={() => dispatch({ type: 'reset' })}>重置</button>
    </view>
  );
}
```

### 4. 跨组件状态共享 (Context)

适用于全局或跨组件状态。

```tsx
import { createContext, useContext, useState } from '@doubao-dev/framework';

// 1. 创建 Context
interface ThemeContextValue {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextValue | null>(null);

// 2. Provider 组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  const toggleTheme = () => {
    setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. 使用 Context
function ThemedButton() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error('必须在 ThemeProvider 内使用');

  const { theme, toggleTheme } = context;

  return (
    <button
      className={theme === 'light' ? 'light-button' : 'dark-button'}
      onClick={toggleTheme}
    >
      切换主题
    </button>
  );
}

// 4. 应用中使用
function App() {
  return (
    <ThemeProvider>
      <view>
        <ThemedButton />
      </view>
    </ThemeProvider>
  );
}
```

### 5. 自定义 Hook

封装可复用的状态逻辑。

```tsx
import { useState, useEffect } from '@doubao-dev/framework';
import { request } from '@doubao-dev/framework/api';

// 自定义 Hook：数据获取
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        setError('');
        const response = await request({ url, method: 'GET' });
        setData(response.data as T);
      } catch (err) {
        setError(err instanceof Error ? err.message : '请求失败');
      } finally {
        setLoading(false);
      }
    };

    void fetchData();
  }, [url]);

  return { data, loading, error };
}

// 使用自定义 Hook
function UserProfile({ userId }: { userId: string }) {
  const { data: user, loading, error } = useFetch<User>(`https://api.example.com/users/${userId}`);

  if (loading) return <text>加载中...</text>;
  if (error) return <text>错误: {error}</text>;
  if (!user) return <text>无数据</text>;

  return (
    <view>
      <text>{user.name}</text>
      <text>{user.email}</text>
    </view>
  );
}
```

---

## 🧭 页面导航模式

### 1. 打开新页面

```tsx
import { navigateTo, redirectTo, reLaunch } from '@doubao-dev/framework/api';

// 保留当前页面，打开详情页
function openFullPage() {
  navigateTo({
    url: '/pages/user-profile/index?userId=12345'
  });
}

// 替换当前页面
function openSettingsPage() {
  redirectTo({
    url: '/pages/settings/index'
  });
}

// 清空页面栈并回到首页
function relaunchHome() {
  reLaunch({
    url: '/pages/home/index'
  });
}
```

这里的 `url` 使用目标 Page 路径，通常和 `src/pages/<page-name>/index.tsx` 对应，例如
`/pages/detail/index`。如需传参，可继续拼接 query string。

### 2. 页面间传参

```tsx
import { navigateTo } from '@doubao-dev/framework/api';

function buildPageUrl(pagePath: string, params: Record<string, string | number | boolean>) {
  const query = Object.entries(params)
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
    .join('&');

  return query ? `${pagePath}?${query}` : pagePath;
}

// 发送方
function navigateWithParams() {
  navigateTo({
    url: buildPageUrl('/pages/detail/index', {
      id: '123',
      name: '商品名称',
      price: 99.99
    })
  });
}

// 接收方 (src/pages/detail/index.tsx)
import { useViewData } from '@doubao-dev/framework';

interface DetailPageData {
  id: string;
  name: string;
  price: number;
}

export default function DetailPage() {
  const { id, name, price } = useViewData<DetailPageData>();

  return (
    <view>
      <text>ID: {id}</text>
      <text>名称: {name}</text>
      <text>价格: {price}</text>
    </view>
  );
}
```

### 3. 返回上一页

```tsx
import { navigateBack } from '@doubao-dev/framework/api';

function closeCurrentPage() {
  navigateBack();
}
```

### 4. 页面栈管理

```tsx
import { navigateBack } from '@doubao-dev/framework/api';

// 场景 1: 返回上一页
function goBack() {
  navigateBack();
}

// 场景 2: 返回多级页面
function goBackTwoPages() {
  navigateBack({ delta: 2 });
}
```

### 5. 页面生命周期导航处理

```tsx
import { useDestroy, useHide, useShow } from '@doubao-dev/framework';

export default function Page() {
  useShow(() => {
    console.log('页面显示');
  });
  useHide(() => {
    console.log('页面隐藏');
  });
  useDestroy(() => {
    console.log('页面销毁');
  });

  return <view>页面内容</view>;
}
```

### 6. 九宫格页面导航

```tsx
import { navigateTo } from '@doubao-dev/framework/api';

// 打开九宫格页面
function openGridPage() {
  navigateTo({
    url: '/pages/app-grid/index'
  });
}

// 九宫格页面定义
export default function GridPage() {
  const apps = [
    { id: 'app1', name: '应用1', icon: '🎯', path: '/pages/app1/index' },
    { id: 'app2', name: '应用2', icon: '📱', path: '/pages/app2/index' },
    { id: 'app3', name: '应用3', icon: '⚡', path: '/pages/app3/index' }
  ];

  return (
    <view className="grid-container">
      {apps.map(app => (
        <view key={app.id} className="grid-item" onClick={() => navigateTo({ url: app.path })}>
          <text className="icon">{app.icon}</text>
          <text className="name">{app.name}</text>
        </view>
      ))}
    </view>
  );
}
```

---

## 🎯 最佳实践

### API 集成

1. **统一错误处理** - 封装 API 请求函数，统一处理错误
2. **请求取消** - 组件卸载时取消未完成的请求
3. **缓存策略** - 合理使用缓存减少网络请求
4. **加载状态** - 始终提供加载和错误状态反馈

### 状态管理

1. **最小化状态** - 只存储必要的状态，能计算的不存储
2. **状态提升** - 共享状态提升到最近的公共父组件
3. **避免过度渲染** - 使用 useMemo、useCallback 优化性能
4. **状态持久化** - 重要状态考虑持久化到本地存储

### 页面导航

1. **合理的页面层级** - 避免页面栈过深（建议不超过 3-4 层）
2. **参数验证** - 接收页面参数时进行验证和默认值处理
3. **返回结果** - 关闭页面时返回必要的结果数据
4. **生命周期管理** - 合理使用生命周期钩子管理资源

---

## 📚 延伸阅读

- **Page/Widget 基础** → [./page-widget-basics.md](./page-widget-basics.md)
- **组件开发完整指南** → [../guides/component-development.md](../guides/component-development.md)
- **豆包智能服务的端能力 API 使用配方** → [./doubao-agentic-service-api-recipes.md](./doubao-agentic-service-api-recipes.md)
- **Page 组件配方** → [./page-widget-recipes.md](./page-widget-recipes.md)
- **Framework 核心入口、生命周期和 Hooks** → [../framework/core.md](../framework/core.md)
- **豆包智能服务的端能力 API 速查** → [../doubao-agentic-service-api/quick-reference.md](../doubao-agentic-service-api/quick-reference.md)
- **最佳实践** → [../guides/best-practices.md](../guides/best-practices.md)
