坚持终有回报:React 组件与本地存储同步
您是否曾花费数分钟创建完美的响应,却因误点或意外刷新而化为乌有?我们常常注重优化性能和打造美观的用户界面,但用户体验又如何呢?这样的经历常常让我们想要愤怒地放弃。在自动保存功能在许多应用程序中流行之前,这种情况屡见不鲜。
还记得每隔几分钟就手动保存一次 Word 文档,以免浪费几个小时的写作时间吗?从那时起,我们已经取得了长足的进步。但是你的 Web 应用呢?保存用户输入并跨会话维护状态对于某些体验至关重要。
React 中的本地存储
什么是本地存储?
本地存储是一个强大的 Web API,它允许你的 Web 应用在 Web 浏览器中存储键值对,且不设过期日期。这种客户端存储机制为 Web 应用带来了诸多优势:
- 持久性:即使浏览器窗口关闭后数据仍然可用,从而实现跨会话的无缝用户体验。
- 容量:本地存储通常为每个域提供 5-10 MB 的数据存储,超越了 cookie 的限制。
- 简单:通过简单的 API,本地存储易于实现和使用,只需最少的设置。
- 性能:访问本地存储比发出服务器请求更快,从而减少了频繁访问的数据的延迟。
- 离线功能:即使用户处于离线状态,应用程序也可以利用本地存储来提供有限的功能。
为什么要将组件状态与本地存储同步?
使用本地存储同步 React 组件状态,为常见的 Web 应用程序挑战提供了强大的解决方案。这种方法弥合了内存状态和客户端持久存储之间的差距,带来了以下几个关键优势:
- 跨会话状态持久化:通过将组件状态存储在本地存储中,您的 Web 应用即使在浏览器刷新或关闭后也能保持用户进度。这种持久化功能可以提升用户体验,尤其是在表单或多步骤流程等几乎可以预见的情况下。
- 未保存更改的备份:实现本地存储同步可以作为意外数据丢失的备份,在用户与您的 Web 应用交互时自动保存用户输入。这意味着您的状态将在组件卸载 -> 挂载和应用刷新之间保持不变。
实现本地存储同步:一个简单的钩子
让我们看一个使用名为 useLocalStorageState() 的自定义钩子的非常简单的实际示例:
import { useState, useEffect } from 'react';
const useLocalStorageState = <T,>(
key: string,
defaultValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] => {
const [state, setState] = useState<T>(() => {
const storedValue = localStorage.getItem(key);
return storedValue !== null ? JSON.parse(storedValue) : defaultValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
}, [key, state]);
return [state, setState];
};
// Example usage
const TextEditor: React.FC = () => {
const [text, setText] = useLocalStorageState<string>('editorText', '');
return (
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Start typing..."
/>
);
};
这个钩子允许我们轻松地将任何组件状态与本地存储同步。请记住,这是一个非常简单的示例,如果 localStorage 值存在,它将始终首先使用它。
现实世界的例子
Kollabe:在敏捷回顾中保留用户输入
最近,我和我的团队在我的 Web 应用Kollabe上进行了一次 Sprint 回顾。我们进行了一次破冰活动,每个人都要画出自己最喜欢的行星。一些团队成员不小心点击了提交按钮,而不是点击了“提交”,结果意外丢失了他们精美的杰作。这是一个糟糕的用户体验,不过,添加一个简单的本地存储同步功能就轻松解决了这个问题。

其他平台:Claude 和 ChatGPT
但不仅仅是我!其他你最熟悉的平台也都在这么做。想象一下,在 ChatGPT 或 Claude 上创建一个很长的提示,然后它就消失了。你也可能会愤怒地退出,从现在开始选择使用他们的竞争对手。

何时使用本地存储同步(以及何时不使用)
- 非常适合保存需要大量精力的用户输入
- 您的应用程序中的每个状态都不需要
- 考虑便利性和性能之间的权衡
- 请勿存储敏感信息。本地存储未加密且无有效期。任何能够物理访问同一设备的人员都可以轻松访问。
性能考虑
性能考虑:平衡持久性和速度
虽然使用本地存储同步组件状态有很多好处,但务必考虑其潜在的性能影响。了解这些影响并实施缓解策略,可以确保在持久性和速度之间取得最佳平衡。
潜在的性能影响
- 增加读/写操作:与本地存储的频繁同步会导致大量的读写操作,可能会影响您的 Web 应用程序的响应能力。
- 解析开销:本地存储仅存储字符串,因此需要对复杂的数据结构进行解析和字符串化 JSON。对于较大的对象,此过程的计算开销会非常高。
- 存储限制:浏览器通常将每个域名的本地存储空间限制为 5-10 MB。超出此限制可能会导致错误和数据丢失。
为了提高性能,您可以考虑将保存的内容去抖动到本地存储。这对于输入尤其重要。
import { useState, useEffect, useCallback } from 'react';
import debounce from 'lodash/debounce';
const useLocalStorageState = <T,>(key: string, defaultValue: T, delay = 300) => {
const [state, setState] = useState<T>(() => {
const storedValue = localStorage.getItem(key);
return storedValue !== null ? JSON.parse(storedValue) : defaultValue;
});
const debouncedSync = useCallback(
debounce((value: T) => {
localStorage.setItem(key, JSON.stringify(value));
}, delay),
[key, delay]
);
useEffect(() => {
debouncedSync(state);
}, [state, debouncedSync]);
return [state, setState] as const;
};
未来的改进
有很多方法可以改进这个钩子,但这实际上取决于你的需求是简单还是复杂。以下是一些示例:
- 实现一个带有元数据(例如 created_at 或 accessible_at)的缓存对象。不过,此时或许值得使用其他库。
- 在启动时清除过期的缓存对象,以防止死对象随着时间的推移占用大量空间。
- 允许存储对象和字符串
- 允许优先使用默认值,而不是本地存储值。如果您的输入也被用作
editMode
现有值的替代值,这将非常有用。
就是这样!
将 React 组件状态与本地存储同步是一项非常有用的技术,可以真正提升 Web 应用的用户体验。我建议你对你的应用程序进行一次审核,看看是否也能从中受益!
另外,我还要毫不掩饰地打个广告🔌。如果你在敏捷开发团队工作,并且使用工具进行在线会议,比如计划扑克或回顾会议,不妨看看我的免费工具Kollabe!
鏂囩珷鏉ユ簮锛�https://dev.to/mattlewandowski93/persistence-pays-off-react-components-with-local-storage-sync-2bfk