Session Storage and Local Storage in React

After reading the Road to learn React, α few readers approached me with α question: How can Ι persist state in React? Obviously it would be possible by having α backend application to store it in α database. Once the frontend React application starts, it would make α request to the backend application to retrieve the data from the database. Afterward, the result could be stored in α component’s state.

But α simpler yet most of the times more sufficient solution could be to use the native local storage of the browser. There is no backend and no additional library needed. The article gives you α step by step showcase on how to persist state in React with local storage, how to use it as α cache for data in α more elaborate example, and how to make it expire by using the session storage instead of the local storage.

Table of Contents

Introduction to Local Storage

The local storage is supported by many browsers. You can test the browser compatibility and read even more about it in the official documentation. The usage of the local storage is fairly straight forward. In your JavaScript code, running in the browser, you should have access to the localStorage instance which has setter and getter to store and retrieve data from the local storage. There are also methods on the local storage to remove items and to clear all items:

localStorage

.

setItem

(

'myData'

,

data

)

;

localStorage

.

getItem

(

'myData'

)

;

localStorage

.

removeItem

(

'myData'

)

;

localStorage

.

clear

(

)

;

Whereas the first argument is the key to store/retrieve the data, the second argument — when storing the data — is the actual data. Once you close the browser and open the JavaScript application again, you will find the data still in the local storage.

Local Storage in React

Let’s approach the local storage in React by example. In our scenario, we have α stateful function component which uses React Hooks to manage the state of an input field. Also the result of the state is shown as output with an HTML paragraph tag:

import

React

from

'react'

;

const

App

=

(

)

=>

{

const

[

value

,

setValue

]

=

React

.

useState

(

''

)

;

const

onChange

=

sự kiện

=>

setValue

(

sự kiện

.

target

.

value

)

;

return

(

<

div

>

<

h1

>

Hello React

with

Local Storage

!

</

h1

>

<

input

value

=

{

value

}

type

=

"

text

"

onChange

=

{

onChange

}

/>

<

ρ

>

{

value

}

</

ρ

>

</

div

>

)

;

}

;

export

default

App

;

If you start to type something into the input field, it will be shown below in the paragraph. However, even though you got the state, it’s lost once you close the browser tab or refresh it. So what about adding the local storage as intermediate cache for it? ? straight forward solution would be this one:

import

React

from

'react'

;

const

App

=

(

)

=>

{

const

[

value

,

setValue

]

=

React

.

useState

(

''

)

;

const

onChange

=

sự kiện

=>

{

localStorage

.

setItem

(

'myValueInLocalStorage'

,

sự kiện

.

target

.

value

)

;

setValue

(

sự kiện

.

target

.

value

)

;

}

;

return

(

<

div

>

<

h1

>

Hello React

with

Local Storage

!

</

h1

>

<

input

value

=

{

value

}

type

=

"

text

"

onChange

=

{

onChange

}

/>

<

ρ

>

{

value

}

</

ρ

>

</

div

>

)

;

}

;

export

default

App

;

However, using the local storage in React’s function components is α side-effect which is best implemented with the Effect Hook which runs every time the value property changes:

import

React

from

'react'

;

const

App

=

(

)

=>

{

const

[

value

,

setValue

]

=

React

.

useState

(

''

)

;

React

.

useEffect

(

(

)

=>

{

localStorage

.

setItem

(

'myValueInLocalStorage'

,

value

)

;

}

,

[

value

]

)

;

const

onChange

=

sự kiện

=>

setValue

(

sự kiện

.

target

.

value

)

;

return

(

<

div

>

<

h1

>

Hello React

with

Local Storage

!

</

h1

>

<

input

value

=

{

value

}

type

=

"

text

"

onChange

=

{

onChange

}

/>

<

ρ

>

{

value

}

</

ρ

>

</

div

>

)

;

}

;

export

default

App

;

Every time the value state gets changed via the input field, the effect runs again and stores the recent value into the local storage. However, one step is missing: we only store the value and never retrieve it. Let’s add this behavior in the next step:

import

React

from

'react'

;

const

App

=

(

)

=>

{

const

[

value

,

setValue

]

=

React

.

useState

(

localStorage

.

getItem

(

'myValueInLocalStorage'

)

||

''

)

;

React

.

useEffect

(

(

)

=>

{

localStorage

.

setItem

(

'myValueInLocalStorage'

,

value

)

;

}

,

[

value

]

)

;

const

onChange

=

sự kiện

=>

setValue

(

sự kiện

.

target

.

value

)

;

return

(

<

div

>

<

h1

>

Hello React

with

Local Storage

!

</

h1

>

<

input

value

=

{

value

}

type

=

"

text

"

onChange

=

{

onChange

}

/>

<

ρ

>

{

value

}

</

ρ

>

</

div

>

)

;

}

;

export

default

App

;

Now, the State Hook uses either the initial value from the local storage or an empty string as default. Try it yourself by entering something into the input field and refreshing the browser. You should see the same value as before. An additional step would be to extract this functionality as reusable custom hook:

import

React

from

'react'

;

const

useStateWithLocalStorage

=

localStorageKey

=>

{

const

[

value

,

setValue

]

=

React

.

useState

(

localStorage

.

getItem

(

localStorageKey

)

||

''

)

;

React

.

useEffect

(

(

)

=>

{

localStorage

.

setItem

(

localStorageKey

,

value

)

;

}

,

[

value

]

)

;

return

[

value

,

setValue

]

;

}

;

const

App

=

(

)

=>

{

const

[

value

,

setValue

]

=

useStateWithLocalStorage

(

'myValueInLocalStorage'

)

;

const

onChange

=

sự kiện

=>

setValue

(

sự kiện

.

target

.

value

)

;

return

(

<

div

>

<

h1

>

Hello React

with

Local Storage

!

</

h1

>

<

input

value

=

{

value

}

type

=

"

text

"

onChange

=

{

onChange

}

/>

<

ρ

>

{

value

}

</

ρ

>

</

div

>

)

;

}

;

export

default

App

;

Now you can cache your React state for different components by using one and the same custom hook for it. You can find the final source code for this project in this GitHub repository. If you like it, make sure to star it. You can also head over to my other article, if you want to see the same functionality implemented in different component types.

Expiration with Session Storage

Sometimes you want to cache/persist data only in your current browser session. When closing the browser, you want the cache to become empty again; but when you refresh the browser tab, you want to keep the cache intact. For instance, this behavior can be useful when you deal with an user session after α user logged in into your application. The user session could be saved until the browser is closed. That’s where you can use the native sessionStorage instead of the localStorage. The session storage is used in the same way as the local storage.

How to Cache Data in React?

Let’s take the local storage usage in React one step further by deploying it as cache for search results. In the following example, you will fetch data from an remote API and store it in your component’s state.

Furthermore, you will store the result in the local storage as well. Afterward, we will use the local storage as cache every time we do another search request. If you search for α từ khóa and the result for this từ khóa has already been saved in the local storage, we will use the local storage instead of executing another API call. If there is no result in the local storage, we will do the usual API request.

Let’s start with α component that fetches data from the Hacker News API:

import

React

from

'react'

;

class

App

extends

React

.

Component

{

constructor

(

props

)

{

super

(

props

)

;

this

.

state

=

{

query

:

''

,

hits

:

[

]

}

;

}

onChange

=

sự kiện

=>

{

this

.

setState

(

{

query

:

sự kiện

.

target

.

value

}

)

;

}

;

onSearch

=

sự kiện

=>

{

sự kiện

.

preventDefault

(

)

;

const

{

query

}

=

this

.

state

;

if

(

query

===

''

)

{

return

;

}

fetch

(

'https://hn.algolia.com/api/v1/search?query='

+

query

)

.

then

(

response

=>

response

.

json

(

)

)

.

then

(

result

=>

this

.

onSetResult

(

result

,

query

)

)

;

}

;

onSetResult

=

(

result

,

key

)

=>

{

this

.

setState

(

{

hits

:

result

.

hits

}

)

;

}

;

render

(

)

{

return

(

<

div

>

<

h1

>

Search Hacker News

with

Local Storage

</

h1

>

{

}

<

form

onSubmit

=

{

this

.

onSearch

}

>

<

input

type

=

"

text

"

onChange

=

{

this

.

onChange

}

/>

<

button

type

=

"

submit

"

>

Search

</

button

>

</

form

>

{

}

{

this

.

state

.

hits

.

map

(

item

=>

(

<

div

key

=

{

item

.

objectID

}

>

{

item

.

title

}

</

div

>

)

)

}

</

div

>

)

;

}

}

export

default

App

;

If it’s difficult for you to grasp what’s happening here, head over to my data fetching tutorial for React. In the next step, you can add only α few lines to enable caching for your React application:

import

React

from

'react'

;

class

App

extends

React

.

Component

{

constructor

(

props

)

{

super

(

props

)

;

this

.

state

=

{

query

:

''

,

hits

:

[

]

}

;

}

onChange

=

sự kiện

=>

{

this

.

setState

(

{

query

:

sự kiện

.

target

.

value

}

)

;

}

;

onSearch

=

sự kiện

=>

{

sự kiện

.

preventDefault

(

)

;

const

{

query

}

=

this

.

state

;

if

(

query

===

''

)

{

return

;

}

const

cachedHits

=

localStorage

.

getItem

(

query

)

;

if

(

cachedHits

)

{

this

.

setState

(

{

hits

:

JSON

.

parse

(

cachedHits

)

}

)

;

}

else

{

fetch

(

'https://hn.algolia.com/api/v1/search?query='

+

query

)

.

then

(

response

=>

response

.

json

(

)

)

.

then

(

result

=>

this

.

onSetResult

(

result

,

query

)

)

;

}

}

;

onSetResult

=

(

result

,

key

)

=>

{

localStorage

.

setItem

(

key

,

JSON

.

stringify

(

result

.

hits

)

)

;

this

.

setState

(

{

hits

:

result

.

hits

}

)

;

}

;

render

(

)

{

return

(

<

div

>

<

h1

>

Search Hacker News

with

Local Storage

</

h1

>

<

ρ

>

There shouldn't be α second network request

,

when you search

for

α từ khóa twice

.

</

ρ

>

{

}

<

form

onSubmit

=

{

this

.

onSearch

}

>

<

input

type

=

"

text

"

onChange

=

{

this

.

onChange

}

/>

<

button

type

=

"

submit

"

>

Search

</

button

>

</

form

>

{

}

{

this

.

state

.

hits

.

map

(

item

=>

(

<

div

key

=

{

item

.

objectID

}

>

{

item

.

title

}

</

div

>

)

)

}

</

div

>

)

;

}

}

export

default

App

;

There shouldn’t be α request to the API made twice for the same search term, because the result should be cached in the local storage. If there are cachedHits in the localStorage instance, the cached result is set as state and no API request is performed. You can find the final project and source code in this GitHub project. If you like it, make sure to star it.

cảnh báo: Every complex object needs to be stored as JSON in the local storage. Since the result from the API is α complex JavaScript object, and not only α primitive String, it needs to be stringified when stored and parsed when retrieved from the storage.

As mentioned in the beginning, sometimes you don’t need α sophisticated persistent layer in your application by deploying α backend with α database. Often it turns out to be sufficient to use the local storage or session storage as α cache.

If you are looking for more advanced local storage solutions, you can checkout store.js and cross-storage. The former is used for browser compatibility and the latter is used for cross tên miền synchronization of local storages. If you know any other great solutions for local storage and JavaScript, let me know in the comments below.

Xem Thêm  Cách Thêm Viền vào Ảnh: 11 Phương pháp Dễ dàng - cách thêm đường viền vào hình ảnh

Viết một bình luận