Skip to content

useCloudStorage

提供 Telegram 云存储功能的 Hook,实现跨设备数据同步,类似于 useState 但数据会持久化并在用户的所有设备间同步。

导入

typescript
import { useCloudStorage, useCloudStorageKeys } from '@xcloud/ui-telegram';

示例

API

useCloudStorage

typescript
const [value, setValue, { loading, error, remove, isAvailable }] = useCloudStorage<T>(key, defaultValue);

参数

参数类型必填描述
keystring存储的键名
defaultValueT默认值

返回值

一个包含三个元素的数组:

索引类型描述
0T | undefined当前值
1(value: T) => Promise<void>更新值的函数
2StorageOptions选项对象

StorageOptions

属性类型描述
loadingboolean是否正在加载
errorError | null错误信息
remove() => Promise<void>删除值的函数
isAvailableboolean云存储是否可用

useCloudStorageKeys

typescript
const { keys, loading, error, refresh, isAvailable } = useCloudStorageKeys();

返回值

属性类型描述
keysstring[]所有存储的键名
loadingboolean是否正在加载
errorError | null错误信息
refresh() => Promise<void>刷新键列表
isAvailableboolean云存储是否可用

使用示例

基础用法 - 字符串存储

存储和获取字符串值:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function ThemeSettings() {
  const [theme, setTheme, { loading }] = useCloudStorage('theme', 'light');

  if (loading) return <div>Loading...</div>;

  return (
    <select value={theme} onChange={(e) => setTheme(e.target.value)}>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
    </select>
  );
}

存储数字

存储计数器或其他数字值:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function Counter() {
  const [count, setCount] = useCloudStorage<number>('counter', 0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

存储对象

存储复杂的对象数据:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

interface UserPreferences {
  language: string;
  notifications: boolean;
  theme: 'light' | 'dark';
}

function Preferences() {
  const [prefs, setPrefs] = useCloudStorage<UserPreferences>('preferences', {
    language: 'en',
    notifications: true,
    theme: 'light',
  });

  const updateLanguage = (lang: string) => {
    setPrefs({ ...prefs, language: lang });
  };

  const toggleNotifications = () => {
    setPrefs({ ...prefs, notifications: !prefs.notifications });
  };

  return (
    <div>
      <select value={prefs.language} onChange={(e) => updateLanguage(e.target.value)}>
        <option value="en">English</option>
        <option value="zh">中文</option>
      </select>
      <label>
        <input
          type="checkbox"
          checked={prefs.notifications}
          onChange={toggleNotifications}
        />
        Enable Notifications
      </label>
    </div>
  );
}

存储数组

存储列表数据:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function TodoList() {
  const [todos, setTodos] = useCloudStorage<string[]>('todos', []);
  const [input, setInput] = useState('');

  const addTodo = () => {
    if (input.trim()) {
      setTodos([...todos, input.trim()]);
      setInput('');
    }
  };

  const removeTodo = (index: number) => {
    setTodos(todos.filter((_, i) => i !== index));
  };

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>
            {todo}
            <button onClick={() => removeTodo(index)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

删除数据

删除云存储中的数据:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function UserData() {
  const [user, setUser, { remove }] = useCloudStorage('user', null);

  const handleLogout = async () => {
    await remove(); // 删除用户数据
    // 跳转到登录页
  };

  return (
    <div>
      {user && (
        <div>
          <p>Welcome, {user.name}!</p>
          <button onClick={handleLogout}>Logout</button>
        </div>
      )}
    </div>
  );
}

错误处理

处理存储错误:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function Settings() {
  const [settings, setSettings, { loading, error }] = useCloudStorage('settings', {});

  const handleUpdate = async (newSettings) => {
    try {
      await setSettings(newSettings);
    } catch (err) {
      console.error('Failed to save settings:', err);
      alert('Failed to save settings');
    }
  };

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>...</div>;
}

检查可用性

在不支持的环境中降级处理:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

function App() {
  const [data, setData, { isAvailable }] = useCloudStorage('data', null);

  if (!isAvailable) {
    // 降级到 localStorage
    const fallbackData = localStorage.getItem('data');
    return <div>Using fallback storage</div>;
  }

  return <div>Using cloud storage</div>;
}

获取所有键名

查看云存储中的所有键:

tsx
import { useCloudStorageKeys } from '@xcloud/ui-telegram';

function StorageManager() {
  const { keys, loading, refresh } = useCloudStorageKeys();

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <h3>Stored Keys ({keys.length}):</h3>
      <ul>
        {keys.map((key) => (
          <li key={key}>{key}</li>
        ))}
      </ul>
      <button onClick={refresh}>Refresh</button>
    </div>
  );
}

游戏进度保存

保存游戏进度并跨设备同步:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';

interface GameProgress {
  level: number;
  score: number;
  unlockedItems: string[];
}

function Game() {
  const [progress, setProgress] = useCloudStorage<GameProgress>('game_progress', {
    level: 1,
    score: 0,
    unlockedItems: [],
  });

  const levelUp = () => {
    setProgress({
      ...progress,
      level: progress.level + 1,
    });
  };

  const addScore = (points: number) => {
    setProgress({
      ...progress,
      score: progress.score + points,
    });
  };

  return (
    <div>
      <p>Level: {progress.level}</p>
      <p>Score: {progress.score}</p>
      <button onClick={levelUp}>Level Up</button>
      <button onClick={() => addScore(100)}>+100 Score</button>
    </div>
  );
}

表单草稿自动保存

自动保存表单内容:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';
import { useEffect, useState } from 'react';

function ArticleEditor() {
  const [draft, setDraft] = useCloudStorage('article_draft', {
    title: '',
    content: '',
  });
  const [localDraft, setLocalDraft] = useState(draft);

  // 自动保存 - 防抖
  useEffect(() => {
    const timer = setTimeout(() => {
      setDraft(localDraft);
    }, 1000); // 1秒后保存

    return () => clearTimeout(timer);
  }, [localDraft, setDraft]);

  return (
    <div>
      <input
        value={localDraft.title}
        onChange={(e) =>
          setLocalDraft({ ...localDraft, title: e.target.value })
        }
        placeholder="Title"
      />
      <textarea
        value={localDraft.content}
        onChange={(e) =>
          setLocalDraft({ ...localDraft, content: e.target.value })
        }
        placeholder="Content"
      />
    </div>
  );
}

多语言设置

存储用户语言偏好:

tsx
import { useCloudStorage } from '@xcloud/ui-telegram';
import { useEffect } from 'react';
import i18n from 'i18next';

function LanguageSelector() {
  const [language, setLanguage] = useCloudStorage('language', 'en');

  useEffect(() => {
    if (language) {
      i18n.changeLanguage(language);
    }
  }, [language]);

  return (
    <select value={language} onChange={(e) => setLanguage(e.target.value)}>
      <option value="en">English</option>
      <option value="zh">中文</option>
      <option value="es">Español</option>
      <option value="fr">Français</option>
    </select>
  );
}

最佳实践

1. 使用 TypeScript 类型

为存储的数据定义明确的类型:

tsx
interface Settings {
  theme: 'light' | 'dark';
  fontSize: number;
  notifications: boolean;
}

const [settings, setSettings] = useCloudStorage<Settings>('settings', {
  theme: 'light',
  fontSize: 14,
  notifications: true,
});

2. 提供默认值

始终提供合理的默认值:

tsx
// ✅ 好的做法
const [theme, setTheme] = useCloudStorage('theme', 'light');

// ❌ 不好的做法
const [theme, setTheme] = useCloudStorage('theme'); // 可能为 undefined

3. 处理加载状态

在数据加载完成前显示加载指示器:

tsx
const [data, setData, { loading }] = useCloudStorage('data', null);

if (loading) {
  return <LoadingSpinner />;
}

return <DataDisplay data={data} />;

4. 避免存储大量数据

每个键最多只能存储 4KB 数据:

tsx
// ✅ 好的做法
const [settings, setSettings] = useCloudStorage('settings', {});

// ❌ 不好的做法 - 可能超过 4KB
const [entireDatabase, setEntireDatabase] = useCloudStorage('database', {
  users: [...],
  posts: [...],
  comments: [...],
});

5. 使用有意义的键名

使用清晰的键名方便调试和维护:

tsx
// ✅ 好的做法
const [theme, setTheme] = useCloudStorage('user_preferences_theme', 'light');
const [lang, setLang] = useCloudStorage('user_preferences_language', 'en');

// ❌ 不好的做法
const [theme, setTheme] = useCloudStorage('t', 'light');
const [lang, setLang] = useCloudStorage('l', 'en');

6. 防抖频繁更新

避免在短时间内频繁写入:

tsx
// 使用防抖
const [value, setValue] = useState('');
const [cloudValue, setCloudValue] = useCloudStorage('value', '');

useEffect(() => {
  const timer = setTimeout(() => {
    setCloudValue(value);
  }, 500);

  return () => clearTimeout(timer);
}, [value]);

存储限制

  • 键数量: 最多 1024 个键
  • 键长度: 每个键名最多 128 字节
  • 值大小: 每个值最多 4096 字节 (4KB)
  • 总大小: 所有键值对总共最多 4MB

数据同步

  • 数据会自动在用户的所有设备间同步
  • 同步通常在几秒内完成
  • 离线时的更改会在重新联网后同步
  • 同一键在不同设备上的并发更改,最后的写入会胜出

注意事项

  • 云存储仅在 Telegram 环境中可用
  • 数据与 Telegram 账号关联,不同用户数据隔离
  • 不要存储敏感信息(如密码、令牌等)
  • 数据会被序列化为 JSON,不支持函数、Symbol 等特殊类型
  • 使用 isAvailable 检查功能可用性,在不支持的环境中提供降级方案

相关 API

基于 MIT 许可发布