Add listen parameter to usePersistedState hook (#5134)

GitOrigin-RevId: d40f942164403a31ffbb10336227eef59a57febf
This commit is contained in:
Alf Eaton 2021-09-29 12:26:00 +01:00 committed by Copybot
parent 388d9c0f1e
commit e76b7696da
2 changed files with 69 additions and 2 deletions

View file

@ -1,7 +1,14 @@
import { useState, useCallback } from 'react'
import { useState, useCallback, useEffect } from 'react'
import localStorage from '../../infrastructure/local-storage'
function usePersistedState(key, defaultValue) {
/**
* @param {string} key
* @param {any} [defaultValue]
* @param {boolean} [listen]
*
* @returns {[any, function]}
*/
function usePersistedState(key, defaultValue, listen = false) {
const [value, setValue] = useState(() => {
return localStorage.getItem(key) ?? defaultValue
})
@ -24,6 +31,24 @@ function usePersistedState(key, defaultValue) {
[key, defaultValue]
)
useEffect(() => {
if (listen) {
const listener = event => {
if (event.key === key) {
// note: this value is read via getItem rather than from event.newValue
// because getItem handles deserializing the JSON that's stored in localStorage.
setValue(localStorage.getItem(key))
}
}
window.addEventListener('storage', listener)
return () => {
window.removeEventListener('storage', listener)
}
}
}, [key, listen])
return [value, updateFunction]
}

View file

@ -138,4 +138,46 @@ describe('usePersistedState', function () {
expect(localStorage.getItem(key)).to.equal('foobar')
})
it('handles syncing values via storage event', async function () {
const key = 'test:sync'
localStorage.setItem(key, 'foo')
expect(window.Storage.prototype.setItem).to.have.callCount(1)
// listen for storage events
const storageEventListener = sinon.stub()
window.addEventListener('storage', storageEventListener)
const Test = () => {
const [value, setValue] = usePersistedState(key, 'bar', true)
useEffect(() => {
setValue('baz')
}, [setValue])
return <div>{value}</div>
}
render(<Test />)
screen.getByText('baz')
expect(window.Storage.prototype.getItem).to.have.callCount(1)
expect(window.Storage.prototype.removeItem).to.have.callCount(0)
expect(window.Storage.prototype.setItem).to.have.callCount(2)
expect(localStorage.getItem(key)).to.equal('baz')
expect(storageEventListener).to.have.callCount(0)
// set the new value in localStorage
localStorage.setItem(key, 'cat')
// dispatch a "storage" event and check that it's picked up by the hook
window.dispatchEvent(new StorageEvent('storage', { key }))
await screen.findByText('cat')
expect(storageEventListener).to.have.callCount(1)
})
})