useLocalStorage
KnowlegeBase 知识库
使用本地存储的 React Hook:useLocalStorage
useLocalStorage
是一个自定义的 React Hook,它允许你在你的 React 应用中使用 Web 浏览器的本地存储(localStorage)。本地存储允许你在用户的浏览器中存储数据,即使用户关闭浏览器或者重启电脑,这些数据也会被保留。
useLocalStorage
Hook 接受两个参数:一个键名(key
)和一个初始值(initialValue
)。它返回一个包含两个元素的数组:一个是存储的值,另一个是一个用于更新这个值的函数。
本知识库要解释的代码如下:
ts
import { useEffect, useState } from 'react'
export const useLocalStorage = <T>(
key: string,
initialValue: T
): [T, (value: T) => void] => {
const [storedValue, setStoredValue] = useState(initialValue)
useEffect(() => {
// Retrieve from localStorage
const item = window.localStorage.getItem(key)
if (item) {
setStoredValue(JSON.parse(item))
}
}, [key])
const setValue = (value: T) => {
// Save state
setStoredValue(value)
// Save to localStorage
window.localStorage.setItem(key, JSON.stringify(value))
}
return [storedValue, setValue]
}
代码来源:https://github.com/vercel/ai-chatbot/blob/main/lib/hooks/use-local-storage.ts
其他相关代码:usehooks-ts https://usehooks-ts.com/react-hook/use-local-storage
函数定义
useLocalStorage
的定义如下:
ts
export const useLocalStorage = <T>(
key: string,
initialValue: T
): [T, (value: T) => void] => {
// ...
}
在这个定义中,<T>
是一个泛型参数,表示存储的值的类型。key
是一个字符串,表示在本地存储中存储数据的键名。initialValue
是类型为 T
的初始值。
函数的返回类型是一个元组(tuple),包含两个元素:一个类型为 T
的值,和一个接受类型为 T
的参数并且没有返回值的函数。
在 TypeScript 中,元组(tuple)是一种特殊的数组,它有固定数量的元素,每个元素的类型都是已知的,而且元素的类型不必相同。
输入参数、输出、=>
这是一个名为 useLocalStorage
的 TypeScript 函数的定义,它使用了泛型 T
。
输入参数:
key: string
:这是一个字符串,表示要在本地存储(localStorage)中存储数据的键名。initialValue: T
:这是一个类型为T
的值,表示如果在本地存储中没有找到对应的键,应该返回的初始值。
输出:
这个函数返回一个元组(tuple),包含两个元素:
第一个元素的类型为
T
,表示从本地存储中获取的值。如果在本地存储中没有找到对应的键,这个值会是initialValue
。第二个元素是一个函数,它接受一个类型为
T
的参数,没有返回值(void
)。这个函数用于更新存储在本地存储中的值。
所以,你可以这样使用 useLocalStorage
函数:
typescript
const [myValue, setMyValue] = useLocalStorage<number>('myKey', 0);
在这个例子中,myValue
是从本地存储中获取的值(或者是初始值 0
),setMyValue
是一个函数,用于更新这个值。
箭头函数的基本语法是:
javascript
const myFunction = (parameters) => {
// function body
}
如果箭头函数只有一个参数,你可以省略括号:
javascript
const myFunction = parameter => {
// function body
}
如果箭头函数的函数体只有一条语句,你可以省略大括号,并且这条语句的结果会被自动返回:
javascript
const myFunction = parameter => parameter * 2;
泛型参数 <T>
TypeScript docs: https://www.typescriptlang.org/docs/handbook/2/generics.html
在 TypeScript 中,<T>
是一个泛型参数的声明。泛型是一种创建可重用组件的方式,这些组件可以适用于多种类型。在这种情况下,T
是一个类型变量,它代表任何类型。
在 useLocalStorage
函数中,<T>
允许你指定存储在本地存储中的值的类型。例如,
- 你可以使用
useLocalStorage<number>
来创建一个存储数字的 Hook, - 或者使用
useLocalStorage<string>
来创建一个存储字符串的 Hook。
这样,TypeScript 就可以在编译时检查你的代码,确保你正确地使用了本地存储的值。例如,如果你使用 useLocalStorage<number>
,然后试图将一个字符串存储到本地存储中,TypeScript 就会给出一个错误。
Hook 实现
首先,它使用 useState
创建一个状态变量 storedValue
和一个更新这个状态的函数 setStoredValue
。
然后,它使用 useEffect
在组件挂载时从本地存储中获取值。它接受一个函数和一个依赖数组作为参数。当依赖数组中的值改变时,这个函数会被执行。在这个例子中,依赖数组只包含 key
,所以当 key
改变时,这个函数会被执行。
在 useEffect
的函数中,它首先尝试从本地存储中获取键为 key
的值。如果找到了这个值,它会使用 JSON.parse
将这个值从一个 JSON 字符串转换为 JavaScript 值,并使用 setStoredValue
更新状态。
最后,定义了一个 setValue
函数,用于更新状态和本地存储中的值。这个函数接受一个新的值作为参数,使用 setStoredValue
更新状态,然后使用 window.localStorage.setItem
将新的值存储到本地存储中。
返回值是一个包含 storedValue
和 setValue
的数组。
如何使用这一 Hook
你可以这样使用 useLocalStorage
:
ts
const [myValue, setMyValue] = useLocalStorage<number>('myKey', 0);
在这个例子中,myValue
是从本地存储中获取的值(或者是初始值 0
),setMyValue
是一个函数,用于更新这个值。
实际使用示例:
ts
const [_, setNewChatId] = useLocalStorage('newChatId', id)
附:TypeScript 的泛型
https://www.typescriptlang.org/docs/handbook/2/generics.html
TypeScript 是 JavaScript 的一个超集,它添加了静态类型检查的功能,使得开发者能够在编译阶段就捕获到潜在的错误。泛型(Generics)是 TypeScript 中一个强大的特性,它允许开发者编写可重用的组件,这些组件能够与多种类型一起工作,而不是仅限于单一的类型。
泛型通过类型参数化,使得函数、类或接口能够适用于多种数据类型。这类似于编程中的多态性,但泛型是在编译时而非运行时确定具体类型。泛型的主要优势在于代码的复用性和类型安全。
泛型函数
泛型函数是泛型最常见的使用场景。例如,一个简单的泛型函数 identity
,它接受一个参数并返回相同的值。在不使用泛型的情况下,我们可能需要为函数指定一个具体的类型,如 number
或 string
。但是,使用泛型后,我们可以创建一个可以处理任何类型的 identity
函数。
ts
function identity<Type>(arg: Type): Type {
return arg;
}
在这个例子中,Type
是一个类型变量,它代表了任何可能的类型。当我们调用这个函数时,可以显式地指定类型参数,或者让 TypeScript 编译器根据传入的参数自动推断类型。
泛型参数默认值
TypeScript 允许为泛型参数指定默认值,这样在调用泛型函数或构造泛型类时,就可以省略对应的类型参数。
ts
declare function create<T extends HTMLElement = HTMLDivElement, U = T[]>
(element?: T, children?: U): Container<T, U>;
在这个例子中,T
有一个默认值 HTMLDivElement
,而 U
的默认值是 T[]
。
进一步讨论与优化
try...catch
ts
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
export const useLocalStorage = <T>(
key: string,
defaultValue: T
): [T, Dispatch<SetStateAction<T>>] => {
const [value, setValue] = useState<T>(defaultValue);
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
if (item) {
setValue(JSON.parse(item));
}
} catch (error) {
console.log(error);
}
}, [key]);
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
Dispatch<SetStateAction<T>>
在React中,Dispatch<SetStateAction<T>>
这样的写法是为了明确指定useState hook中setter函数的类型。让我们来解释一下这个写法的含义:
Dispatch<A>
:Dispatch是一个泛型类型,它接受一个类型参数A,表示一个dispatch函数,用于接收一个action并执行相应的操作。在这里,Dispatch<SetStateAction<T>>
表示一个dispatch函数,用于接收一个SetStateAction类型的action,用于更新状态。SetStateAction<S>
:SetStateAction是一个泛型类型,它接受一个类型参数S,表示可以用来更新状态的动作。它可以是一个新的状态值S,也可以是一个函数(prevState: S) => S
,用于根据先前的状态计算新的状态。在这里,SetStateAction<T>
表示可以更新类型为T的状态的动作。
因此,这样的写法可以确保useState hook中的setter函数接受的是一个符合SetStateAction<T>
类型的动作,从而保证状态更新的正确性和类型安全性。
在React中,使用这样的写法可以带来以下优势:
- 类型安全性:通过明确定义setter函数的类型为
Dispatch<SetStateAction<T>>
,可以在编译时捕获潜在的类型错误。这样可以避免意外传递错误类型的值给setter函数,提高代码的可靠性。 - 代码自文档化:可以让其他开发人员更容易地理解代码。通过类型定义,可以清晰地了解setter函数的期望参数类型,从而提高代码的可读性和可维护性。
- 智能提示:IDE可以提供更准确的智能提示和自动补全功能。这有助于开发人员更快地编写代码,并减少潜在的错误。
总之,这样的类型可以提高代码的可靠性、可读性和开发效率,是在React中推荐的最佳实践之一。
(本页部分内容由 AI 辅助生成,仅供参考,使用前请仔细鉴别。)