温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

业务层hooks怎么封装useSessionStorage

发布时间:2022-08-26 10:32:31 来源:亿速云 阅读:140 作者:iii 栏目:开发技术

业务层hooks怎么封装useSessionStorage

在现代前端开发中,React Hooks 已经成为了一种非常流行的状态管理方式。Hooks 提供了一种更简洁、更灵活的方式来管理组件的状态和副作用。其中,useStateuseEffect 是最常用的 Hooks 之一。然而,在实际开发中,我们经常需要将状态持久化到浏览器的 sessionStorage 中,以便在页面刷新或重新加载时能够恢复之前的状态。本文将详细介绍如何在业务层封装一个 useSessionStorage Hook,以便在项目中更方便地使用 sessionStorage

1. 什么是 sessionStorage

sessionStorage 是浏览器提供的一种本地存储机制,它允许我们在浏览器会话期间存储数据。与 localStorage 不同,sessionStorage 中的数据只在当前会话期间有效,当用户关闭浏览器标签页或窗口时,数据将被清除。

sessionStorage 的主要特点包括:

  • 会话级别存储:数据只在当前浏览器会话期间有效。
  • 键值对存储:数据以键值对的形式存储,键和值都必须是字符串。
  • 同源策略sessionStorage 受同源策略限制,不同源的数据无法共享。

2. 为什么需要封装 useSessionStorage

在实际开发中,我们经常需要将组件的状态持久化到 sessionStorage 中。例如,用户在表单中输入的数据、用户的偏好设置等。如果每次都需要手动操作 sessionStorage,代码会变得冗长且难以维护。因此,封装一个 useSessionStorage Hook 可以让我们更方便地管理 sessionStorage 中的数据。

封装 useSessionStorage 的好处包括:

  • 简化代码:通过封装,我们可以将 sessionStorage 的操作抽象成一个 Hook,减少重复代码。
  • 提高可维护性:将 sessionStorage 的逻辑集中在一个地方,便于维护和修改。
  • 增强可读性:使用 Hook 的方式可以让代码更加直观和易读。

3. 如何封装 useSessionStorage

接下来,我们将详细介绍如何封装一个 useSessionStorage Hook。我们将从基本的实现开始,逐步增加功能,最终实现一个功能完善的 useSessionStorage Hook。

3.1 基本实现

首先,我们来实现一个最基本的 useSessionStorage Hook。这个 Hook 将允许我们存储和读取 sessionStorage 中的数据。

import { useState, useEffect } from 'react';

function useSessionStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.sessionStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

export default useSessionStorage;

3.1.1 代码解析

  • useState:我们使用 useState 来管理 sessionStorage 中的数据。初始值通过 window.sessionStorage.getItem(key) 获取,如果 sessionStorage 中没有对应的值,则使用 initialValue

  • setValue:这是一个更新 sessionStorage 数据的函数。它接受一个值或一个函数作为参数。如果传入的是一个函数,那么这个函数将接收当前的值作为参数,并返回一个新的值。然后,我们将这个新值存储到 sessionStorage 中。

  • JSON.parseJSON.stringify:由于 sessionStorage 只能存储字符串,我们需要使用 JSON.stringify 将对象转换为字符串,使用 JSON.parse 将字符串转换回对象。

3.1.2 使用示例

import React from 'react';
import useSessionStorage from './useSessionStorage';

function App() {
  const [name, setName] = useSessionStorage('name', 'John Doe');

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
}

export default App;

在这个示例中,我们使用 useSessionStorage 来存储用户的名字。当用户在输入框中输入名字时,名字会被存储到 sessionStorage 中。即使页面刷新,名字也会被保留。

3.2 处理复杂数据类型

在上面的实现中,我们假设 sessionStorage 中存储的是简单的数据类型(如字符串、数字、布尔值等)。然而,在实际开发中,我们可能需要存储更复杂的数据类型,如数组、对象等。为了处理这些复杂数据类型,我们需要对 useSessionStorage 进行一些改进。

3.2.1 改进代码

import { useState, useEffect } from 'react';

function useSessionStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.sessionStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  useEffect(() => {
    const handleStorageChange = (event) => {
      if (event.key === key) {
        setStoredValue(JSON.parse(event.newValue));
      }
    };

    window.addEventListener('storage', handleStorageChange);

    return () => {
      window.removeEventListener('storage', handleStorageChange);
    };
  }, [key]);

  return [storedValue, setValue];
}

export default useSessionStorage;

3.2.2 代码解析

  • useEffect:我们使用 useEffect 来监听 sessionStorage 的变化。当 sessionStorage 中的数据发生变化时,我们会更新组件的状态。这样可以确保当其他标签页或窗口修改了 sessionStorage 中的数据时,当前页面也能及时更新。

  • handleStorageChange:这是一个事件处理函数,当 sessionStorage 发生变化时,它会检查变化的键是否与当前 Hook 使用的键一致。如果一致,则更新组件的状态。

3.2.3 使用示例

import React from 'react';
import useSessionStorage from './useSessionStorage';

function App() {
  const [todos, setTodos] = useSessionStorage('todos', []);

  const addTodo = () => {
    const newTodo = { id: Date.now(), text: 'New Todo' };
    setTodos([...todos, newTodo]);
  };

  return (
    <div>
      <h1>Todos</h1>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
      <button onClick={addTodo}>Add Todo</button>
    </div>
  );
}

export default App;

在这个示例中,我们使用 useSessionStorage 来存储一个待办事项列表。当用户点击“Add Todo”按钮时,一个新的待办事项会被添加到列表中,并存储到 sessionStorage 中。

3.3 处理过期时间

在某些情况下,我们可能希望 sessionStorage 中的数据在一定时间后自动过期。例如,用户的登录状态可能只需要在一段时间内有效。为了实现这个功能,我们可以对 useSessionStorage 进行进一步的改进。

3.3.1 改进代码

import { useState, useEffect } from 'react';

function useSessionStorage(key, initialValue, expirationMinutes) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.sessionStorage.getItem(key);
      if (item) {
        const { value, timestamp } = JSON.parse(item);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        return isExpired ? initialValue : value;
      }
      return initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      const item = {
        value: valueToStore,
        timestamp: Date.now(),
      };
      window.sessionStorage.setItem(key, JSON.stringify(item));
    } catch (error) {
      console.error(error);
    }
  };

  useEffect(() => {
    const handleStorageChange = (event) => {
      if (event.key === key) {
        const { value, timestamp } = JSON.parse(event.newValue);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        setStoredValue(isExpired ? initialValue : value);
      }
    };

    window.addEventListener('storage', handleStorageChange);

    return () => {
      window.removeEventListener('storage', handleStorageChange);
    };
  }, [key, expirationMinutes, initialValue]);

  return [storedValue, setValue];
}

export default useSessionStorage;

3.3.2 代码解析

  • expirationMinutes:我们新增了一个 expirationMinutes 参数,用于指定数据的过期时间(以分钟为单位)。

  • timestamp:在存储数据时,我们不仅存储数据的值,还存储当前的时间戳。这样,在读取数据时,我们可以检查数据是否已经过期。

  • isExpired:在读取数据时,我们检查当前时间与存储时间戳的差值是否超过了过期时间。如果超过了,则返回初始值,否则返回存储的值。

3.3.3 使用示例

import React from 'react';
import useSessionStorage from './useSessionStorage';

function App() {
  const [token, setToken] = useSessionStorage('token', null, 5); // 5分钟后过期

  const login = () => {
    setToken('fake-token');
  };

  const logout = () => {
    setToken(null);
  };

  return (
    <div>
      <h1>Token: {token || 'No Token'}</h1>
      <button onClick={login}>Login</button>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

export default App;

在这个示例中,我们使用 useSessionStorage 来存储用户的登录令牌。令牌在5分钟后会自动过期。当用户点击“Login”按钮时,令牌会被存储到 sessionStorage 中;当用户点击“Logout”按钮时,令牌会被清除。

3.4 处理同步更新

在某些情况下,我们可能希望在多个组件之间共享 sessionStorage 中的数据,并且当一个组件更新数据时,其他组件也能及时响应。为了实现这个功能,我们可以使用 useEffect 来监听 sessionStorage 的变化,并在数据变化时更新组件的状态。

3.4.1 改进代码

import { useState, useEffect } from 'react';

function useSessionStorage(key, initialValue, expirationMinutes) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.sessionStorage.getItem(key);
      if (item) {
        const { value, timestamp } = JSON.parse(item);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        return isExpired ? initialValue : value;
      }
      return initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      const item = {
        value: valueToStore,
        timestamp: Date.now(),
      };
      window.sessionStorage.setItem(key, JSON.stringify(item));
      window.dispatchEvent(new Event('storage'));
    } catch (error) {
      console.error(error);
    }
  };

  useEffect(() => {
    const handleStorageChange = (event) => {
      if (event.key === key) {
        const { value, timestamp } = JSON.parse(event.newValue);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        setStoredValue(isExpired ? initialValue : value);
      }
    };

    window.addEventListener('storage', handleStorageChange);

    return () => {
      window.removeEventListener('storage', handleStorageChange);
    };
  }, [key, expirationMinutes, initialValue]);

  return [storedValue, setValue];
}

export default useSessionStorage;

3.4.2 代码解析

  • window.dispatchEvent:在 setValue 函数中,我们使用 window.dispatchEvent 手动触发一个 storage 事件。这样,其他组件也能监听到 sessionStorage 的变化,并及时更新状态。

3.4.3 使用示例

import React from 'react';
import useSessionStorage from './useSessionStorage';

function ComponentA() {
  const [count, setCount] = useSessionStorage('count', 0);

  return (
    <div>
      <h1>Component A: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

function ComponentB() {
  const [count, setCount] = useSessionStorage('count', 0);

  return (
    <div>
      <h1>Component B: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

function App() {
  return (
    <div>
      <ComponentA />
      <ComponentB />
    </div>
  );
}

export default App;

在这个示例中,我们有两个组件 ComponentAComponentB,它们共享同一个 sessionStoragecount。当其中一个组件更新 count 时,另一个组件也会自动更新。

3.5 处理错误和边界情况

在实际开发中,我们可能会遇到一些错误和边界情况,例如 sessionStorage 不可用、存储空间不足等。为了确保 useSessionStorage 的健壮性,我们需要对这些情况进行处理。

3.5.1 改进代码

import { useState, useEffect } from 'react';

function useSessionStorage(key, initialValue, expirationMinutes) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      if (typeof window === 'undefined') {
        return initialValue;
      }

      const item = window.sessionStorage.getItem(key);
      if (item) {
        const { value, timestamp } = JSON.parse(item);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        return isExpired ? initialValue : value;
      }
      return initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      if (typeof window === 'undefined') {
        console.warn('sessionStorage is not available');
        return;
      }

      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      const item = {
        value: valueToStore,
        timestamp: Date.now(),
      };
      window.sessionStorage.setItem(key, JSON.stringify(item));
      window.dispatchEvent(new Event('storage'));
    } catch (error) {
      console.error(error);
    }
  };

  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const handleStorageChange = (event) => {
      if (event.key === key) {
        const { value, timestamp } = JSON.parse(event.newValue);
        const isExpired = Date.now() - timestamp > expirationMinutes * 60 * 1000;
        setStoredValue(isExpired ? initialValue : value);
      }
    };

    window.addEventListener('storage', handleStorageChange);

    return () => {
      window.removeEventListener('storage', handleStorageChange);
    };
  }, [key, expirationMinutes, initialValue]);

  return [storedValue, setValue];
}

export default useSessionStorage;

3.5.2 代码解析

  • typeof window === 'undefined':我们检查 window 对象是否存在,以确保代码在服务器端渲染(SSR)时不会报错。如果 window 对象不存在,则直接返回初始值。

  • console.warn:在 setValue 函数中,如果 sessionStorage 不可用,我们会输出一个警告信息。

3.5.3 使用示例

import React from 'react';
import useSessionStorage from './useSessionStorage';

function App() {
  const [theme, setTheme] = useSessionStorage('theme', 'light');

  return (
    <div>
      <h1>Theme: {theme}</h1>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </div>
  );
}

export default App;

在这个示例中,我们使用 useSessionStorage 来存储用户选择的主题。如果 sessionStorage 不可用,代码仍然可以正常运行,并且不会报错。

4. 总结

通过本文的介绍,我们详细讲解了如何在业务层封装一个 useSessionStorage Hook。我们从基本的实现开始,逐步增加了处理复杂数据类型、过期时间、同步更新以及错误处理等功能。最终,我们实现了一个功能完善、健壮的 useSessionStorage Hook,可以在实际项目中方便地使用。

封装 useSessionStorage 不仅简化了代码,还提高了代码的可维护性和可读性。希望本文对你理解和应用 React Hooks 有所帮助。在实际开发中,你可以根据具体需求对 useSessionStorage 进行进一步的定制和扩展。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI