I am using React 18.2.0, where I have a BodyComponent
that stores results
from an API call into states and filters
that are used in the call.
The BodyComponent
retrieves the results
and the filters
from the Session Storage at load with a hook, and updates the sessionStorage
when the results
are updated.
export default function BodyComponent(){
const [results, setResults] = useState([]);
const [filters, setFilters] = useState([]);
useEffect(() => {
if (window.sessionStorage.getItem('results') !== null) {
setResults(JSON.parse(window.sessionStorage.getItem('results')));
}
if (window.sessionStorage.getItem('filters') !== null && filters.length === 0) {
setFilters(JSON.parse(window.sessionStorage.getItem('filters')));
}
}, []);
useEffect(() => {
window.sessionStorage.setItem('results', JSON.stringify(results));
}, [results]);
return (
...
<FilterComponent filters={filters} setFilters={setFilters}/>
...
)
}
The filter component updates the filters to the session storage :
export default function FilterComponent(props) {
const {filters, setFilters} = props;
useEffect(() => {
window.sessionStorage.setItem('filters', JSON.stringify(filters));
}, [filters]);
return (
...
)
}
When I navigate to another React route and back to the route that loads BodyComponent
, filters
are saved to the session storage, and BodyComponent
successfully retrieves them and passes them to FilterComponent
at load.
However, the same is not true for results
, which are saved to the session storage but not set to their State on load.
I have tried the following :
handling the states and the sessionStorage on the parent component of BodyComponent, thinking it'd be a problem of asynchronous state updates. When I did this, not only it didn't solve the problem, but the filters weren't loaded correctly anymore either.
Updating the session storage manually everywhere I'm updating it (Which is bad pratice, but i've tried.), thinking it'd be a problem because the hooks don't detect the updates correctly. This didn't change anything.
Copying the useEffect writing to session storage into each Component involved. Thid didn't change anything either.
What am I missing?
const [results, setResults] = useState([]);
useEffect(() => {
console.log(results); // results is empty on the first render
window.sessionStorage.setItem('results', JSON.stringify(results));
}, [results]);
When the effect is executed for the first time, results
is empty so you reset your results after the initial render.
From the documentation, useState
can take an initializer function to compute the initial state (this function will be called only once when the component is initialized).
function load(key) {
const item = window.sessionStorage.getItem(key);
return item != null ? JSON.parse(item) : [];
}
export default function BodyComponent(){
const [results, setResults] = useState(() => load('results'));
const [filters, setFilters] = useState(() => load('filters'));
useEffect(() => {
window.sessionStorage.setItem('results', JSON.stringify(results));
}, [results]);
....
}