It is difficult for me to understand this custom React hook. The code works, but I understand only some parts. It is a useLocalStorage hook. I will be grateful for any feedback.
This is my App.js file.
import "./App.css";
import useLocalStorage from "./useLocalStorage";
function App() {
const [name, setName] = useLocalStorage("NAME", "");
return (
<>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<div>My name is {name}</div>
</>
);
}
export default App;
This is my useLocalStorage.js file.
import { useEffect, useState } from "react";
function getSavedValue(key, initialValue) {
const savedValue = JSON.parse(localStorage.getItem(key));
if (savedValue) return savedValue;
return initialValue;
}
function useLocalStorage(key, initialValue) {
console.log(key);
console.log(initialValue);
const [value, setValue] = useState(() => {
return getSavedValue(key, initialValue);
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
});
return [value, setValue];
}
export default useLocalStorage;
I understand how the savedValue (in useLocalStorage.js) is passed to the App.js file. I just retrieve it with the getSavedValue function, like this.
const [value, setValue] = useState(() => {
return getSavedValue(key, initialValue);
});
And later, I pass it to the App.js with these two lines.
Firstly, I return the value in the useLocalStorage.js
return [value, setValue];
Secondly, I pass the value variable into the name variable in the App.js file.
const [name, setName] = useLocalStorage("NAME", "");
This is how the passing of the savedValue from useLocalStorage.js to App.js works. I think.
But how does it work the other way around? How is the updated name variable passed from App.js to useLocalStorage? It seems to me that only “NAME” and initialValue are passed from App.js to useLocalStorage.js. Like this.
const [name, setName] = useLocalStorage("NAME", "");
As a result, in the useLocalStorage.js, I am able to access the “NAME” and intitialValue(“”) with the console.log statement.
console.log(key);
console.log(initialValue);
And it works. But I do not have this option for the updated name variable. Or do I? How do I access the updated name variable (available in the App.js) in the useLocalStorage.js???
To your first question ("How does the updated name variable get passed from App.js to useLocalStorage"):
The value returned by useLocalStorage
is the same as the useState
it uses internally. In other words,
your current setup is maintaining state in two places: in the useState
(internal to useLocalStorage
) and
in local storage itself.
To your second question ("Do I have access to the updated name variable"):
Yes. Your name
variable is value
.
While your code may have the intended effect, I don't think it works the way you think it does. Here's what will happen on the first render (assuming the key hasn't been set in localStorage yet):
App
component will call useLocalStorage
with "NAME" and "" as parameters.useState
. It is passing a function that is only executed once (on first render). After that, this function is never called again. In this function, you call getSavedValue
. This will try to get the key from local storage and return it if it exists. If not, it will return the initial value.value
(which now is an empty string, the initialValue
) and a setter.value
and setValue
(which it calls name
and setName
) to populate your form.useEffect
hook runs and saves the previously-returned initial value to local storage. Now, the key should be setNow, you start typing in the input. Here's what will happen:
setName
(which recall is actually setValue
) is called. This simply updates the internal state of the localStorage.App
component.useLocalStorage
hook will get called again. This time, it won't call the getSavedValue
function. It will return whatever was set when you called setName
/setValue
in step 1.useEffect
hook fires again in useLocalStorage
, which will save the value to local storage.In a nutshell, you are using localStorage as an out-of-band storage mechanism that is updated independently of the actual component state. If you refresh the page, it will initialize from the local storage (instead of using the initialValue
you pass in).