localStorage
是浏览器网络存储的两种机制之一。它允许用户将数据以键/值对的形式保存在浏览器中供以后使用。
与sessionStorage
机制不同的是,只要当前的浏览器标签在运行,就会在浏览器存储中持续保存数据,localStorage
在浏览器关闭时不会清除数据。
这使得它非常适合于持久化不绑定在当前浏览器标签上的数据。
开发人员在向应用程序添加黑暗模式功能、持久化待办事项或持久化用户的表单输入值等许多用例中,经常实施localStorage
。
在本指南中,我们将介绍如何使用localStorage
,使用React Hooks在浏览器存储中持久化用户的表单输入。我们还将介绍如何创建一个自定义的React Hook来在多个组件之间共享类似的逻辑。
localStorage
与React Hooks的先决条件
要遵循本指南,确保你对React和React Hooks有基本了解。同时,确保你的电脑上安装了Node.js。
最初的localStorage
项目设置
使用一个新鲜的React应用程序,让我们到电脑终端,运行以下命令来创建一个新的React项目。
npx create-react-app localstorage-react-hook
复制代码
一旦项目文件夹生成,用代码编辑器打开它,通过运行npm start
命令启动开发服务器。
该项目应该在浏览器中启动,地址是http://localhost:3000/。
创建一个React表单组件
如前所述,我们将使用localStorage
,将用户的表单输入持久化在浏览器存储中。
像每个React应用程序一样,我们的重点是在src
文件夹。所以,让我们删除src
里面的所有文件,并在src
里面创建一个index.js
文件,以避免前端中断。
然后,在index.js
中添加以下代码。
import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
// styles
import "./app.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
复制代码
注意,我们导入了一个CSS文件来为应用程序添加样式。所以,让我们在src
文件夹中创建一个app.css
文件夹。
从localstorage-react-hook-project中复制样式并将其添加到app.css
文件中。
接下来,在src
文件夹中创建一个components
文件夹,用来存放组件文件。然后,添加一个App.js
文件和一个Form1.js
文件。App.js
文件是根和父组件,而Form1.js
将存放表单输入。
在components/App.js
文件中添加以下代码。
import Form1 from "./Form1";
const App = () => {
return (
<div className="container">
<h1>localStorage with React hooks</h1>
<Form1 />
</div>
);
};
export default App;
复制代码
最后,将这段代码添加到components/Form1.js
文件中。
import { useState } from "react";
const Form1 = () => {
const [name, setName] = useState("");
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Full name"
aria-label="fullname"
/>
<input type="submit" value="Submit"></input>
</form>
);
};
export default Form1;
复制代码
保存文件后,测试该项目,你应该看到这样的效果。
上面的代码是React中表单输入的最简单实现。通过使用useState
React Hook来控制该组件,我们在每次按键时都保持输入状态的更新,如上图所示。
但是,一旦我们触发了页面刷新,输入数据就会被清除,这是预料之中的。
为了保持输入数据,使其在页面重新加载或后续重访时可用,我们必须将数据保存在localStorage
。
将表单输入数据保存在localStorage
localStorage
,我们就可以访问浏览器的Storage
对象。Storage
对象有一些方法可用于保存、读取和删除数据,以及其他许多操作。
要查看Storage
方法的列表,请打开浏览器控制台并输入localStorage
。按回车键后,这些方法将在Storage
对象的prototype
。
使用setItem()
方法
为了将表单输入数据存储在浏览器存储中,我们必须使用以下语法来调用setItem()
存储方法。
localStorage.setItem("key", "value")
复制代码
浏览器存储只接受数据类型的字符串。因此,对于不同数据类型的值,如对象或数组,我们必须使用JSON.stringify()
将其转换为JSON字符串。
使用useEffect
钩子来执行侧面效果
我们还可以使用useEffect
React Hook来执行副作用,比如在浏览器存储中存储数据。这使得这个Hook成为调用setItem
方法的完美场所。
打开components/Form1.js
文件,在return
语句上方添加以下代码。
useEffect(() => {
// storing input name
localStorage.setItem("name", JSON.stringify(name));
}, [name]);
复制代码
确保像这样从React导入useEffect
。
import { useState, useEffect } from "react";
复制代码
在这里,我们已经分配了一个键,"name"
,和一个来自状态变量的动态值,即name
。
name
状态变量的初始值默认为一个空字符串。
const [name, setName] = useState("");
复制代码
当把字符串数据保存到存储器中时,在setItem
中使用JSON.stringify
是可选的。
localStorage.setItem("name", JSON.stringify(name));
复制代码
然而,如果值是不同的数据类型,如对象或数组,则需要使用JSON.stringify
。
现在,保存文件并测试项目;我们应该看到以下的效果。
在每次按键时,输入值都会保存在本地存储中,因为持有setItem
存储方法的useEffect
Hook会在第一次组件渲染和每次状态改变后运行。
然而,在页面重新加载时,存储中的值会返回到一个空字符串。发生这种情况是因为我们给状态变量name
分配了一个默认的空字符串。因此,React在初始渲染时使用空值。
现在,我们必须在每一点上从存储中获取更新的状态值,并将其指定为默认状态值,而不是指定一个空字符串。
读取数据localStorage
在初始页面加载时,我们必须指定一个访问本地存储的函数,检索保存的值,并使用该值作为默认值,而不是给name
状态变量分配一个空字符串。
使用getItem()
方法
更新components/Form1.js
文件中的useState
Hook。
const [name, setName] = useState(() => {
// getting stored value
const saved = localStorage.getItem("name");
const initialValue = JSON.parse(saved);
return initialValue || "";
});
复制代码
在这里,我们使用getItem()
存储方法,从本地存储中检索数据。代码中使用的JSON.parse()
将返回的JSON字符串从存储中反序列化。
在处理字符串值时,JSON.Stringify
和JSON.parse
都是可选的(正如我们的案例中所看到的)。然而,其他数据类型,如对象和数组,需要它们。
保存文件并测试该项目。输入的数据应该在页面重新加载或以后的页面访问时在表单字段中可用。
创建一个自定义的React Hook来持久化表单输入
有时我们可能想在一个不同的组件中渲染和持久化更多的表单输入,比如一个文本输入和一个复选框输入。
虽然我们可以很容易地从已经创建的组件中复制逻辑并在新的组件中使用它,但这并不总是可行的,特别是如果我们决定创建更多这样的输入。
相反,React允许我们使用自定义Hooks在组件之间提取和分享类似的逻辑。
在这一节中,我们将学习如何创建一个自定义Hook来持久化多个组件中的表单输入。
让我们从创建另一个表单开始。在src/components
文件夹中,创建一个名为Form2.js
的新文件,并添加以下代码。
import { useState } from "react";
const Form2 = () => {
const [name, setName] = useState("");
const [checked, setChecked] = useState(false);
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Full name"
aria-label="fullname"
/>
<label>
<input
type="checkbox"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>{" "}
Not a robot?
</label>
<input type="submit" value="Submit"></input>
</form>
);
};
export default Form2;
复制代码
然后,导入并使用components/App.js
文件中的组件。
// ...
import Form2 from "./Form2";
const App = () => {
return (
<div className="container">
{/* ... */}
<Form2 />
</div>
);
};
export default App;
复制代码
保存文件并在前台查看表单。
与这个表单交互并不能持久化localStorage
中的状态值,因为我们还没有这个逻辑。
所以,让我们定义一个单一的逻辑来管理我们所有的表单输入。
提取localStorage
的逻辑
要开始提取localStorage
的逻辑,在src
文件夹中创建一个名为useLocalStorage.js
的文件,并添加以下代码。
import { useState, useEffect } from "react";
function getStorageValue(key, defaultValue) {
// getting stored value
const saved = localStorage.getItem(key);
const initial = JSON.parse(saved);
return initial || defaultValue;
}
export const useLocalStorage = (key, defaultValue) => {
const [value, setValue] = useState(() => {
return getStorageValue(key, defaultValue);
});
useEffect(() => {
// storing input name
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
复制代码
仔细看一下上面的代码,我们只是从components/Form1.js
文件中提取了存储逻辑。我们没有做什么特别的事情。
通过创建一个名为useLocalStorage
的自定义Hook,我们保持了Form1
组件中的所有存储逻辑。
useLocalStorage
Hook希望有两个参数:key
和defaultValue
。这意味着当我们在不同的组件中调用Hook时,我们希望能传递这些值。
注意,你可以给你的自定义Hook起任何名字,但要确保以use
。
使用useLocalStorage
自定义Hook
在components/Form1.js
文件中,用自定义Hook替换return
语句上面的逻辑,这样你就有了以下内容。
import { useLocalStorage } from "../useLocalStorage";
const Form1 = () => {
const [name, setName] = useLocalStorage("name", "");
return (
<form>
{/* ... */}
</form>
);
};
export default Form1;
复制代码
在导入自定义Hook后,我们可以使用它并传递唯一的键和默认值,在这种情况下,默认值是一个空字符串。
如果我们对components/Form2js
文件中的Form2
组件做同样的事情,我们应该有如下的结果。
import { useLocalStorage } from "../useLocalStorage";
const Form2 = () => {
const [name, setName] = useLocalStorage("name2", "");
const [checked, setChecked] = useLocalStorage("checked", false);
return (
<form>
{/* ... */}
</form>
);
};
export default Form2;
复制代码
保存所有的文件并测试该项目。我们应该能够持久化localStorage
中的所有表单输入。
干得好!
为服务器端渲染的应用程序访问localStorage
的问题
当使用像Next.js这样在服务器端执行代码的框架时,使用localStorage
,会出现一个错误,说明 "窗口没有定义"。
在我们的代码中使用的localStorage
是window
对象的一个内置属性,window.localStorage
。
在我们的代码中,我们在访问localStorage
的时候忽略了window
,因为它是一个全局对象;我们可以选择包括window
对象,因为它是可选的。
现在,这个window
对象在服务器端无法使用,而是在客户端/浏览器上,这就提示了这个错误。
为了在服务器端解决这个错误,请检查window
对象是否被定义。这样,我们的代码就只在有window
的环境中运行。
打开src/useLocalStorage.js
文件,更新getStorageValue()
函数,这样你就有了下面的内容。
function getStorageValue(key, defaultValue) {
// getting stored value
if (typeof window !== "undefined") {
const saved = localStorage.getItem(key);
const initial = saved !== null ? JSON.parse(saved) : defaultValue;
return initial;
}
}
复制代码
不要忘记,我们也在useLocalStorage.js
文件中的useEffect
Hook里面使用了localStorage
。
但在这种情况下,localStorage
是安全的,因为useEffect
Hook只在客户端运行,我们可以访问window
对象。
测试该项目,以确保一切仍按预期工作。
总结
我们已经介绍了如何使用localStorage
,用React Hooks在浏览器中持久化数据。我们还学习了如何创建一个自定义Hook,将组件逻辑提取为可重用的函数。
如果你喜欢这个指南,请在网络上分享它。而且,如果你有任何问题或贡献,请通过评论区分享。
在这里可以找到这个项目的全部源代码。
The postUsing localStorage
with React Hooks appearedfirst on LogRocketBlog.