跳到主要内容

useLocalStorage

管理 localStorage 的 Hook,提供类型安全的本地存储操作,并支持跨标签页同步。

基本用法

实时编辑器
function Demo() {
  const [value, setValue] = useLocalStorage({ key: 'demo-value', defaultValue: '' });

  return (
    <div>
      <Input
        placeholder="输入内容(会自动保存)"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <div style={{ marginTop: '12px', fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
        当前存储的值: {value || '(空)'}
      </div>
      <Button
        onClick={() => setValue('')}
        size="small"
        style={{ marginTop: '12px' }}
      >
        清空
      </Button>
    </div>
  );
}
结果
Loading...

主题持久化

保存用户的主题选择:

实时编辑器
function Demo() {
  const [theme, setTheme] = useLocalStorage({ 
    key: 'app-theme', 
    defaultValue: 'light' 
  });

  return (
    <div>
      <Group spacing="md">
        <Button 
          onClick={() => setTheme('light')}
          variant={theme === 'light' ? 'filled' : 'outline'}
        >
          ☀️ 亮色
        </Button>
        <Button 
          onClick={() => setTheme('dark')}
          variant={theme === 'dark' ? 'filled' : 'outline'}
        >
          🌙 暗色
        </Button>
        <Button 
          onClick={() => setTheme('auto')}
          variant={theme === 'auto' ? 'filled' : 'outline'}
        >
          🔄 自动
        </Button>
      </Group>
      <div
        style={{
          marginTop: '20px',
          padding: '24px',
          backgroundColor: 
            theme === 'dark' ? '#1a1a1a' : 
            theme === 'light' ? '#ffffff' : 
            'var(--ifm-background-surface-color)',
          color: 
            theme === 'dark' ? '#ffffff' : 
            theme === 'light' ? '#000000' : 
            'var(--ifm-font-color-base)',
          border: '1px solid var(--ifm-color-emphasis-300)',
          borderRadius: '8px',
          transition: 'all 0.3s',
        }}
      >
        <h4>当前主题: {theme}</h4>
        <p>刷新页面后主题会保持不变</p>
      </div>
    </div>
  );
}
结果
Loading...

用户偏好设置

保存复杂的用户配置:

实时编辑器
function Demo() {
  const [fontSize, setFontSize] = useLocalStorage({ 
    key: 'font-size', 
    defaultValue: '16' 
  });
  const [notifications, setNotifications] = useLocalStorage({ 
    key: 'notifications', 
    defaultValue: 'true' 
  });

  return (
    <div>
      <div style={{ marginBottom: '20px' }}>
        <label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
          字体大小
        </label>
        <Group spacing="sm">
          {['14', '16', '18', '20'].map((size) => (
            <Button
              key={size}
              size="small"
              variant={fontSize === size ? 'filled' : 'outline'}
              onClick={() => setFontSize(size)}
            >
              {size}px
            </Button>
          ))}
        </Group>
      </div>
      
      <div style={{ marginBottom: '20px' }}>
        <label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
          <input
            type="checkbox"
            checked={notifications === 'true'}
            onChange={(e) => setNotifications(e.target.checked ? 'true' : 'false')}
            style={{ marginRight: '8px' }}
          />
          <span>启用通知</span>
        </label>
      </div>

      <div
        style={{
          padding: '20px',
          backgroundColor: 'var(--ifm-color-emphasis-100)',
          borderRadius: '8px',
          fontSize: `${fontSize}px`,
        }}
      >
        <h4 style={{ margin: '0 0 12px 0' }}>预览区域</h4>
        <p style={{ margin: 0 }}>
          字体大小: {fontSize}px {notifications === 'true' && '· 通知已启用 🔔'}
        </p>
      </div>
    </div>
  );
}
结果
Loading...

表单草稿自动保存

自动保存表单输入:

实时编辑器
function Demo() {
  const [title, setTitle] = useLocalStorage({ key: 'draft-title', defaultValue: '' });
  const [content, setContent] = useLocalStorage({ key: 'draft-content', defaultValue: '' });

  const handleClear = () => {
    setTitle('');
    setContent('');
  };

  return (
    <div>
      <div style={{ marginBottom: '12px' }}>
        <Input
          placeholder="标题"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
      </div>
      <div style={{ marginBottom: '12px' }}>
        <Textarea
          placeholder="内容(自动保存草稿)"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          rows={4}
        />
      </div>
      <Group spacing="md">
        <Button onClick={handleClear} variant="outline">
          清空草稿
        </Button>
      </Group>
      {(title || content) && (
        <div
          style={{
            marginTop: '16px',
            padding: '12px',
            backgroundColor: 'var(--ifm-color-success-lightest)',
            borderRadius: '6px',
            fontSize: '14px',
          }}
        >
          ✓ 草稿已自动保存到本地
        </div>
      )}
    </div>
  );
}
结果
Loading...

函数式更新

支持基于当前值的更新:

实时编辑器
function Demo() {
  const [count, setCount] = useLocalStorage({ key: 'counter', defaultValue: '0' });

  const increment = () => setCount((prev) => String(Number(prev) + 1));
  const decrement = () => setCount((prev) => String(Number(prev) - 1));
  const reset = () => setCount('0');

  return (
    <div style={{ textAlign: 'center' }}>
      <div
        style={{
          fontSize: '48px',
          fontWeight: 'bold',
          marginBottom: '20px',
          color: 'var(--ifm-color-primary)',
        }}
      >
        {count}
      </div>
      <Group spacing="md">
        <Button onClick={decrement}>-</Button>
        <Button onClick={increment}>+</Button>
        <Button onClick={reset} variant="outline">重置</Button>
      </Group>
      <p style={{ marginTop: '16px', fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
        计数会保存到 localStorage
      </p>
    </div>
  );
}
结果
Loading...

API

参数

function useLocalStorage<T extends string>({
key,
defaultValue,
}: {
key: string;
defaultValue?: T;
}): [T, (val: T | ((prevState: T) => T)) => void]
参数说明类型默认值
keylocalStorage 的键名string-
defaultValue默认值Tundefined

返回值

返回一个数组,用法类似 useState

索引说明类型
[0]当前值T
[1]更新函数(val: T | ((prevState: T) => T)) => void

特性

  • 类型安全: 提供 TypeScript 类型支持
  • 跨标签页同步: 自动同步多个标签页的值
  • SSR 友好: 安全处理服务端渲染场景
  • 默认值: 支持设置默认值
  • 函数式更新: 支持基于当前值的更新

使用场景

  • 用户偏好: 保存主题、语言等用户设置
  • 表单草稿: 自动保存表单输入
  • 购物车: 持久化购物车数据
  • 浏览历史: 记录用户浏览历史
  • 临时数据: 存储不需要同步到服务器的临时数据