The use-local-storage-state package makes it easier to persist data in the browser by abstracting the use of localStorage
into a React Hook. It can save data and parse them automatically, and we can use it in multiple places in our app.
In this post, we’ll look at how to use the Hook to help us to more simply use localStorage
in our React apps.
To get started, we install the package by writing:
npm install use-local-storage-state
Then we can use it as follows:
import React, { useEffect } from "react"; import useLocalStorageState from "use-local-storage-state"; export default function App() { const [todos, setTodos] = useLocalStorageState("todos", []); useEffect(() => { setTodos(["eat", "drink"]); }, []); return ( <div className="App"> {todos.map((t, i) => ( <p key={i}>{t}</p> ))} </div> ); }
We imported the useLocalStorageState
Hook from use-local-storage-state, then put the Hook in our App
component. To set the values of the component when the component loads, we use the useEffect
Hook with an empty array in the second argument so that it only loads when App
first renders. Once we do that, todos
has the values from the Hook, and we can render them in the div
we return.
If we look at the localStorage
section of our browser console, which is under the Application tab of the Chrome console, we can see that the key is todos
, which we set as the first argument of useLocalStorageState
. Its value, ["eat", "drink"]
, is the array we called with setTodo
. The Hook stringified the value for us without our using JSON.stringify
, which is convenient.
localStorage
To update the localStorage
value, we can write the following code:
import React, { useEffect, useState } from "react"; import useLocalStorageState from "use-local-storage-state"; export default function App() { const [todos, setTodos] = useLocalStorageState("todos", []); const [todo, setTodo] = useState(""); const onClick = () => { setTodos([...todos, todo]); setTodo(""); }; useEffect(() => { setTodos(["eat", "drink"]); }, []); return ( <div className="App"> <input value={todo} onChange={e => setTodo(e.target.value)} /> <button onClick={onClick}>Create</button> {todos.map((t, i) => ( <p key={i}>{t}</p> ))} </div> ); }
We added an input element and an onClick
handler to add entries to the todos
array. This will automatically be saved in localStorage
since we called the setTodos
function returned by useLocalStorageState
.
To delete a to-do item, we can write:
import React, { useEffect, useState } from "react"; import useLocalStorageState from "use-local-storage-state"; export default function App() { const [todos, setTodos] = useLocalStorageState("todos", []); const [todo, setTodo] = useState(""); const onClick = () => { setTodos([...todos, todo]); setTodo(""); }; const onDelete = index => { const newTodos = todos.filter((_, i) => i !== index); setTodos(newTodos); }; useEffect(() => { setTodos(["eat", "drink"]); }, []); return ( <div className="App"> <input value={todo} onChange={e => setTodo(e.target.value)} /> <button onClick={onClick}>Create</button> {todos.map((t, i) => ( <p key={i}> {t} <button onClick={onDelete.bind(undefined, i)}>Delete</button> </p> ))} </div> ); }
We added an onDelete
function that calls filter
on the todo
array to return a new array without the specified index. We then pass the newTodos
array that’s returned to setTodos
.
To get the index
to the function, we have onDelete.bind(undefined, i)
in our onClick
for the Delete
buttons. bind
returns a new function that has the arguments we want passed in, which, in this case, would be the index of the entry we want to delete.
To add edit functionality, we want to create a component to both display the value and to let us perform the edits. To do that, we write:
import React, { useEffect, useState } from "react"; import { createLocalStorageStateHook } from "use-local-storage-state"; const useTodos = createLocalStorageStateHook("todos"); const Todo = ({ name, index, onDelete }) => { const [todos, setTodos] = useTodos(); const [todo, setTodo] = useState(""); const [editing, setEditing] = useState(false); const save = () => { const newTodos = [...todos]; newTodos[index] = todo; setTodos(newTodos); setEditing(false); }; useEffect(() => { setTodo(name); }, []); return ( <div> {(() => { if (editing) { return ( <p> <input value={todo} onChange={e => setTodo(e.target.value)} /> <button onClick={save}>Save</button> </p> ); } else { return <p>{name}</p>; } })()} <button onClick={() => setEditing(editing => !editing)}>Edit</button> <button onClick={onDelete}>Delete</button> </div> ); }; export default function App() { const [todos, setTodos] = useTodos(); const [todo, setTodo] = useState(""); const onClick = () => { setTodos([...todos, todo]); setTodo(""); }; const onDelete = index => { const newTodos = todos.filter((_, i) => i !== index); setTodos(newTodos); }; return ( <div className="App"> <input value={todo} onChange={e => setTodo(e.target.value)} /> <button onClick={onClick}>Create</button> {todos.map((t, i) => ( <Todo key={i} index={i} name={t} onDelete={onDelete.bind(this, i)} /> ))} </div> ); }
In the code above, we created a Todo
component to hold the to-do item with an input that allows us to edit the to-do. We also called the createLocalStorageStateHook
function so that we can share the localStorage
content in both Todo
and App
. In Todo
and App
, we use the useTodo
Hook that’s returned by createLocalStorageStateHook
.
In the save
of Todo
, we make a copy of the todos
array and set the current value of todo
according to the index
value. We then call setTodo
returned by useTodos
to save the new todo item. Calling setEditing
with false
disables the input.
The onDelete
function in Todo
comes from App
. We call bind
on onDelete
, then pass it in as the value of the onDelete
so that it’s called with the right index. We can now click Edit, enter the new to-do value, and save the new value to localStorage
.
Also, note that we removed the useEffect
Hook in App
so that it retrieves the items from localStorage
instead of resetting them with the value in that Hook. Now we get a complete to-do app that we can add, edit, and delete items with.
The use-local-storage-state package gives us an easy way to use localStorage
in React apps. With it, we can create, update, and delete data with ease.
With this, we no longer need to use JSON.parse
or JSON.stringify
to deal with localStorage
values. All we have to do is call the functions returned by the built-in Hooks, which save the values for us.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]