-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
336 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Storage } from './types' | ||
|
||
export const createMemoryStorage = (): Storage => { | ||
let memory: { [key: string]: string } = {} | ||
const getItem = (key: string) => { | ||
return memory[key] || null | ||
} | ||
const setItem = (key: string, value: string) => { | ||
memory[key] = value | ||
} | ||
const removeItem = (key: string) => { | ||
delete memory[key] | ||
} | ||
const clear = () => { | ||
memory = {} | ||
} | ||
return { getItem, setItem, removeItem, clear } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
export * from './useStorageItem' | ||
export * from './createMemoryStorage' | ||
export * from './useAsync' | ||
export * from './useStorage' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export interface Storage { | ||
getItem: (key: string) => string | null | ||
setItem: (key: string, value: string) => void | ||
removeItem: (key: string) => void | ||
clear: () => void | ||
} | ||
|
||
export type Fn<T> = () => T |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Storage, Fn } from 'types' | ||
import { useStorage } from './useStorage' | ||
import { createMemoryStorage } from './createMemoryStorage' | ||
|
||
export function useAsync<T>( | ||
key: string, | ||
fn: () => Promise<T>, | ||
storage: Storage = useAsync.__storage | ||
) { | ||
const [value, setValue] = useStorage<T>(key, null, storage) | ||
const reload = () => setValue(null) | ||
if (value === null) throw fn().then(setValue) | ||
return [value, reload] as [T, Fn<void>] | ||
} | ||
|
||
useAsync.__storage = createMemoryStorage() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from 'react' | ||
import { Storage, Fn } from './types' | ||
import { createMemoryStorage } from './createMemoryStorage' | ||
import { isFunction } from './utils' | ||
|
||
type Value<T> = T | Fn<T> | null | ||
|
||
export function useStorage<T>( | ||
key: string, | ||
initialValue: Value<T> = null, | ||
storage: Storage = useStorage.__storage | ||
): [T | null, (x: Value<T>) => void] { | ||
let storedString = storage.getItem(key) | ||
const [value, setValue] = React.useState<T | null>( | ||
storedString ? (JSON.parse(storedString) as T) : initialValue | ||
) | ||
React.useEffect(() => { | ||
if (!storedString && initialValue) { | ||
storage.setItem(name, JSON.stringify(initialValue)) | ||
} | ||
}, []) | ||
const set = React.useCallback( | ||
(newValue: Value<T>) => { | ||
if (newValue === null) { | ||
storage.removeItem(key) | ||
setValue(null) | ||
} else { | ||
if (isFunction(newValue)) { | ||
newValue = (newValue as Fn<T>)() | ||
} | ||
storage.setItem(key, JSON.stringify(newValue)) | ||
setValue(newValue) | ||
} | ||
}, | ||
[storage, key] | ||
) | ||
return [value, set] | ||
} | ||
|
||
useStorage.__storage = createMemoryStorage() |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function isFunction(x: any) { | ||
return x && {}.toString.call(x) === '[object Function]' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React, { Suspense } from 'react' | ||
import { act } from 'react-dom/test-utils' | ||
import { render } from '@testing-library/react' | ||
import { useAsync, createMemoryStorage } from '../src' | ||
|
||
const storage = createMemoryStorage() | ||
|
||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) | ||
|
||
const Resolve = ({ data, ms }: any) => { | ||
const [result] = useAsync( | ||
'key', | ||
async () => { | ||
await delay(ms) | ||
return data | ||
}, | ||
storage | ||
) | ||
return <p>{JSON.stringify(result)}</p> | ||
} | ||
|
||
describe('useAsync', () => { | ||
afterEach(storage.clear) | ||
it('renders fallback while waiting for the promise', async () => { | ||
const { container } = render( | ||
<Suspense fallback={<p>Loading...</p>}> | ||
<Resolve data="some data" ms={100} /> | ||
</Suspense> | ||
) | ||
expect(container.innerHTML).toBe('<p>Loading...</p>') | ||
}) | ||
|
||
it('renders the result when the promise resolves', async () => { | ||
let container: any = null | ||
await act(async () => { | ||
container = render( | ||
<Suspense fallback={<p>Loading...</p>}> | ||
<Resolve data="some data" ms={100} /> | ||
</Suspense> | ||
).container | ||
await delay(100) | ||
}) | ||
expect(container.innerHTML).toBe('<p>"some data"</p>') | ||
}) | ||
|
||
it('caches the result for next renders', async () => { | ||
let result: any = null | ||
await act(async () => { | ||
result = render( | ||
<Suspense fallback={<p>Loading...</p>}> | ||
<Resolve data="some data" ms={100} /> | ||
</Suspense> | ||
) | ||
await delay(100) | ||
}) | ||
result.rerender( | ||
<Suspense fallback={<p>Loading...</p>}> | ||
<Resolve data="some data" ms={100} /> | ||
</Suspense> | ||
) | ||
expect(result.container.innerHTML).toBe('<p>"some data"</p>') | ||
}) | ||
}) |
Oops, something went wrong.