# useRequest Hook 使用文档

React Native 项目的统一网络请求 Hook，基于 `ky` 实现，提供了完整的错误处理、Token 认证等功能。

## 特性

- ✅ 基于 `ky` 库，轻量且现代化
- ✅ 自动添加 Token 认证（Bearer Token）
- ✅ 统一的错误处理和错误提示
- ✅ 支持 TypeScript 类型推断
- ✅ 自动处理业务错误码（401、403、404、500等）
- ✅ Token 过期自动清除并提示重新登录
- ✅ 支持跳过认证和自定义错误处理
- ✅ 完美配合 React Query 使用
- ✅ 存储使用 MMKV（高性能）

## 安装

此 Hook 已集成到项目中，无需额外安装。依赖的库：

```json
{
  "ky": "^1.8.2",
  "react-native-mmkv": "^3.3.0"
}
```

## 基础用法

### 1. GET 请求

```tsx
import { useRequest } from '@/hooks';

function UserProfile() {
  const { get } = useRequest();
  const [user, setUser] = useState(null);

  const fetchUser = async () => {
    try {
      const data = await get('/api/user/profile');
      setUser(data);
    } catch (error) {
      // 错误已自动处理
    }
  };

  useEffect(() => {
    fetchUser();
  }, []);

  return <View>{/* UI */}</View>;
}
```

### 2. POST 请求

```tsx
import { useRequest } from '@/hooks';

function UpdateProfile() {
  const { post } = useRequest();

  const updateProfile = async () => {
    try {
      const result = await post('/api/user/update', {
        name: '张三',
        email: 'zhangsan@example.com',
      });
      console.log('更新成功:', result);
    } catch (error) {
      // 错误已自动处理
    }
  };

  return <Button onPress={updateProfile} title="更新资料" />;
}
```

### 3. PUT 和 DELETE 请求

```tsx
import { useRequest } from '@/hooks';

function UserManagement() {
  const { put, delete: del } = useRequest();

  // 更新用户
  const updateUser = async (id: number) => {
    await put(`/api/user/${id}`, { name: '李四' });
  };

  // 删除用户
  const deleteUser = async (id: number) => {
    await del(`/api/user/${id}`);
  };

  return <View>{/* UI */}</View>;
}
```

## 高级用法

### 1. 带查询参数的 GET 请求

```tsx
const { get } = useRequest();

const fetchProducts = async () => {
  const result = await get('/api/products', {
    searchParams: {
      page: 1,
      pageSize: 20,
      category: 'electronics',
    },
  });
};
```

### 2. 跳过认证（登录接口）

```tsx
import { useRequest, setToken } from '@/hooks';

function Login() {
  const { post } = useRequest();

  const handleLogin = async (username: string, password: string) => {
    try {
      // 登录接口不需要 token，使用 skipAuth
      const result = await post<{ token: string }>(
        '/api/auth/login',
        { username, password },
        { skipAuth: true }
      );

      // 保存 token
      if (result.token) {
        setToken(result.token);
        // 导航到首页
      }
    } catch (error) {
      // 错误处理
    }
  };

  return <View>{/* UI */}</View>;
}
```

### 3. 自定义错误处理

```tsx
const { get } = useRequest();

const fetchData = async () => {
  try {
    // 跳过全局错误处理，使用自定义错误处理
    const result = await get('/api/data', {
      skipErrorHandler: true,
    });
  } catch (error) {
    // 自定义错误处理逻辑
    console.error('自定义错误处理:', error);
    Alert.alert('提示', '加载失败，请重试');
  }
};
```

### 4. 配合 React Query 使用

```tsx
import { useQuery } from '@tanstack/react-query';
import { useRequest } from '@/hooks';

function UserList() {
  const { get } = useRequest();

  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['users'],
    queryFn: () => get('/api/users'),
  });

  if (isLoading) return <ActivityIndicator />;
  if (error) return <Text>加载失败</Text>;

  return (
    <View>
      {data.map(user => (
        <Text key={user.id}>{user.name}</Text>
      ))}
      <Button title="刷新" onPress={() => refetch()} />
    </View>
  );
}
```

### 5. 文件上传

```tsx
const { post } = useRequest();

const uploadFile = async (file: File) => {
  const formData = new FormData();
  formData.append('file', file);

  try {
    const result = await post('/api/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    });
    console.log('上传成功:', result);
  } catch (error) {
    console.error('上传失败:', error);
  }
};
```

### 6. 直接使用 http 实例

```tsx
import { http } from '@/hooks';

// 不使用 Hook 的场景下，可以直接使用导出的 http 实例
export async function quickRequest() {
  try {
    const result = await http.get('api/quick-request').json();
    return result;
  } catch (error) {
    console.error('请求失败:', error);
  }
}
```

## Token 管理

### 获取、设置和删除 Token

```tsx
import { getToken, setToken, removeToken } from '@/hooks';

// 获取当前 token
const token = getToken();
console.log('当前token:', token);

// 设置 token（登录成功后）
setToken('your-jwt-token-here');

// 删除 token（退出登录）
removeToken();
```

### 自动 Token 认证

所有请求会自动在 Header 中添加 `Authorization: Bearer {token}`，无需手动设置。

如果需要跳过认证（如登录接口），使用 `skipAuth: true` 选项：

```tsx
await post('/api/login', { username, password }, { skipAuth: true });
```

## 错误处理

### 自动错误处理

Hook 会自动处理以下错误：

- **401**: 认证失败，自动清除 token 并提示重新登录
- **403**: 权限不足
- **404**: 资源不存在
- **500**: 服务器错误
- **网络错误**: 超时、网络连接异常等

### 业务错误码处理

后端返回的业务错误码也会自动处理：

```json
{
  "code": 401,
  "msg": "token已过期",
  "data": null
}
```

### 自定义错误处理

使用 `skipErrorHandler: true` 跳过全局错误处理：

```tsx
try {
  await get('/api/data', { skipErrorHandler: true });
} catch (error) {
  // 自定义错误处理
}
```

## API 响应格式

默认期望后端返回以下格式：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    // 实际数据
  }
}
```

Hook 会自动提取 `data` 字段作为返回值。

## RequestConfig 配置选项

```typescript
interface RequestConfig {
  // ky 原生选项
  headers?: Record<string, string>;
  searchParams?: Record<string, any>;
  timeout?: number;
  retry?: number;
  
  // 自定义选项
  skipErrorHandler?: boolean; // 跳过全局错误处理
  skipAuth?: boolean;         // 跳过认证（不添加 token）
}
```

## TypeScript 类型支持

完整的 TypeScript 类型推断：

```tsx
interface User {
  id: number;
  name: string;
  email: string;
}

const { get } = useRequest();

// result 会被推断为 User 类型
const result = await get<User>('/api/user/1');
console.log(result.name); // 类型安全
```

## 环境变量配置

在 `.env` 文件中配置 API 基础 URL：

```env
API_URL=https://api.example.com
```

## 完整示例

查看 `useRequest.example.tsx` 文件获取更多实际使用示例。

## 常见问题

### Q: 如何修改 Token 存储键？

A: 修改 `useRequest.ts` 中的 `TOKEN_KEY` 常量。

### Q: 如何自定义导航到登录页面？

A: 修改 `tokenFailureHandler` 函数，使用导航 ref 或全局事件实现。

### Q: 支持多个 baseURL 吗？

A: 可以创建多个 ky 实例，每个实例配置不同的 prefixUrl。

### Q: 如何添加全局请求拦截器？

A: 修改 `createKyInstance` 函数中的 `hooks.beforeRequest`。

### Q: 如何禁用自动 Alert 提示？

A: 使用 `skipErrorHandler: true` 跳过全局错误处理。

## 与 H5 axios 版本的区别

| 特性 | H5 (axios) | RN (ky) |
|------|-----------|---------|
| 库 | axios | ky |
| Toast 提示 | vant showToast | React Native Alert |
| 存储 | localStorage | MMKV |
| 导入方式 | import.meta.env | process.env |
| 体积 | 较大 | 更小 |
| 现代化 | 传统 | 现代 Fetch API |

## 参考资料

- [ky 文档](https://github.com/sindresorhus/ky)
- [React Query 文档](https://tanstack.com/query/latest)
- [MMKV 文档](https://github.com/mrousavy/react-native-mmkv)
