历史记录组件

50 阅读3分钟

🧩 一、设计目标

封装一个“历史记录管理组件”,
能让筛选框、表单、搜索输入框等组件:

  • 自动记录用户的输入历史;
  • 支持查看、删除、清空;
  • 数据持久化(localStorage);
  • 与业务组件无侵入式适配。

✅ 典型交互场景:

比如一个搜索框👇

  • 用户输入内容 → 自动存入历史;
  • 点击搜索 → 历史记录更新;
  • 点击输入框 → 展示历史选项;
  • 点击历史记录 → 自动填入。

🧠 二、设计目标拆解

模块职责
存储层提供历史记录增删查封装(基于 localStorage)
逻辑层 Hook / Service管理状态、封装增删逻辑、与组件交互
UI 层组件渲染历史记录下拉框、清空按钮等
适配层与不同组件(输入框、筛选表单)无缝整合

🧱 三、核心设计思路(模块化架构)

 ┌─────────────────────────────┐
 │  UI组件层:HistoryList、Input、Select │
 ├─────────────────────────────┤
 │  逻辑层 Hook:useHistory(key)        │
 ├─────────────────────────────┤
 │  存储层:LocalHistoryService         │
 └─────────────────────────────┘

⚙️ 四、核心实现步骤


1️⃣ 封装一个 LocalStorage 工具类

class LocalHistoryService<T = string> {
  private key: string;
  private limit: number;

  constructor(key: string, limit = 10) {
    this.key = key;
    this.limit = limit;
  }

  get(): T[] {
    const data = localStorage.getItem(this.key);
    return data ? JSON.parse(data) : [];
  }

  add(item: T) {
    const list = this.get();
    // 去重 + 截断
    const newList = [item, ...list.filter(v => v !== item)].slice(0, this.limit);
    localStorage.setItem(this.key, JSON.stringify(newList));
    return newList;
  }

  remove(item: T) {
    const list = this.get().filter(v => v !== item);
    localStorage.setItem(this.key, JSON.stringify(list));
    return list;
  }

  clear() {
    localStorage.removeItem(this.key);
  }
}

✅ 功能:

  • 自动去重;
  • 控制历史条数;
  • 封装增删改查。

2️⃣ 封装逻辑层 Hook(以 React 为例)

import { useState } from "react";

export function useHistory(key: string, limit = 10) {
  const service = new LocalHistoryService<string>(key, limit);
  const [records, setRecords] = useState(service.get());

  const add = (item: string) => setRecords(service.add(item));
  const remove = (item: string) => setRecords(service.remove(item));
  const clear = () => {
    service.clear();
    setRecords([]);
  };

  return { records, add, remove, clear };
}

✅ 封装优点:

  • 任何组件都能复用;
  • 内部与 localStorage 同步;
  • 状态变化即触发重新渲染。

3️⃣ UI层组件(搜索框 + 历史展示)

import React, { useState } from "react";
import { useHistory } from "./useHistory";

export function SearchWithHistory() {
  const { records, add, remove, clear } = useHistory("searchHistory", 8);
  const [value, setValue] = useState("");

  const handleSearch = () => {
    if (value.trim()) add(value.trim());
  };

  return (
    <div className="search-box">
      <input
        value={value}
        onChange={e => setValue(e.target.value)}
        onFocus={() => console.log("显示历史记录")}
        placeholder="搜索内容"
      />
      <button onClick={handleSearch}>搜索</button>

      {records.length > 0 && (
        <ul className="history-list">
          {records.map(item => (
            <li key={item} onClick={() => setValue(item)}>
              {item}
              <button onClick={() => remove(item)}>x</button>
            </li>
          ))}
          <li onClick={clear} className="clear">
            清空历史
          </li>
        </ul>
      )}
    </div>
  );
}

4️⃣ 通用适配(表单 / 筛选框)

任何组件都可以接入这个 Hook:

const Filter = () => {
  const { records, add } = useHistory("filterHistory");
  const [filter, setFilter] = useState("");

  return (
    <select
      value={filter}
      onChange={(e) => {
        const val = e.target.value;
        setFilter(val);
        add(val);
      }}
    >
      <option value="">选择分类</option>
      <option value="A">类型 A</option>
      <option value="B">类型 B</option>
    </select>
  );
};

或者在 Form 组件中统一注册:

function FormExample() {
  const { add } = useHistory("formHistory");

  const handleSubmit = (values: Record<string, string>) => {
    add(JSON.stringify(values));
  };

  return <YourFormComponent onFinish={handleSubmit} />;
}

🧩 五、扩展设计

功能实现方式
✅ 支持多用户在 key 前加 userId 前缀
✅ 支持过期时间存储结构 {value, expire},校验过期
✅ 支持模糊搜索records.filter(v => v.includes(keyword))
✅ 支持全局共享用 React Context / Vue Provide 注入 service
✅ 支持跨页面同步监听 window.storage 事件更新 UI

💡 六、工程化优化建议

  1. 防抖写入: 避免频繁操作 localStorage
    → 使用 lodash.debounce 封装 add。

  2. 封装通用接口:

    interface HistoryStorage<T> {
      get(): T[];
      add(item: T): T[];
      remove(item: T): T[];
      clear(): void;
    }
    

    可以替换存储方式(localStorage / indexedDB)。

  3. 泛型支持: 支持非 string 历史记录(比如对象型表单)。

  4. UI 与逻辑解耦: Hook 负责逻辑,UI 由业务组件决定。


🧠 七、整体架构总结

用户交互
   ↓
useHistory Hook —— 封装状态与逻辑
   ↓
LocalHistoryService —— 操作 localStoragelocalStorage —— 持久化存储

✅ 八、一句话总结

TypeScript + localStorage + Hook 封装
实现一个轻量级“历史记录中间层”,
让表单、筛选框、搜索框都能无缝复用历史功能,
并具备去重、持久化、可清除等工程化特性。