Skip to content

Commit

Permalink
fix(useLocalStorage): 更新实现
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Jul 16, 2020
1 parent e8d53c8 commit 1d44804
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ export {
export { useTitle } from './useTitle'
export { useInterval, UseIntervalResult } from './useInterval'
export { useSearchParam } from './useSearchParam'
export { useLocalStorage } from './useLocalStorage'
export { useLocalStorage, UseLocalStorageResult } from './useLocalStorage'
6 changes: 6 additions & 0 deletions src/react/useLocalStorage.taro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@ describe('useLocalStorage', () => {

const { result: resy } = renderHook(() => useLocalStorage('y', 5))
expect(resy.current[0]).toBe(5)
act(() => resy.current[1](1))
expect(resy.current[0]).toBe(1)
act(() => resy.current[1](prev => prev + 2))
expect(resy.current[0]).toBe(3)
act(() => resy.current[2]())
expect(resy.current[0]).toBe(5)
})
})
70 changes: 46 additions & 24 deletions src/react/useLocalStorage.taro.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,57 @@
import { useLocalStorage as _useLocalStorage, useUpdateEffect } from 'react-use'
import {
useLocalStorage as _useLocalStorage,
UseLocalStorageResult,
} from './useLocalStorage'
import { getStorageSync, removeStorage, setStorage } from '@tarojs/taro'
import { useCallback, useState } from 'react'
import { useLatest, useUpdateEffect } from 'react-use'

export const useLocalStorage: typeof _useLocalStorage = <S>(
key: string,
initialState?: S,
): UseLocalStorageResult<S | undefined> => {
const getLocalStorageItem = useCallback(() => {
try {
const data = getStorageSync(key)
if (data != null) {
return JSON.parse(data)
}
return initialState
} catch {
return initialState
}
}, [key, initialState])

const [state, setState] = useState(getLocalStorageItem)

const latestKey = useLatest(key)
const latestInitialState = useLatest(initialState)
const latestState = useLatest(state)

export const useLocalStorage: typeof _useLocalStorage = (
key,
initialValue,
// 忽略选项
_options,
) => {
const [value, setValue] = useState<ReturnType<typeof _useLocalStorage>[0]>(
() => getStorageSync(key) || initialValue,
)
useUpdateEffect(() => {
setValue(getStorageSync(key) || initialValue)
setState(getLocalStorageItem())
}, [key])
const set: ReturnType<typeof _useLocalStorage>[1] = useCallback(
valueOrSetter => {
if (typeof valueOrSetter === 'function') {
valueOrSetter = valueOrSetter(value)

const set: UseLocalStorageResult<S | undefined>[1] = useCallback(
nextState => {
if (typeof nextState === 'function') {
nextState = (nextState as any)(latestState.current)
}
setState(nextState)
setStorage({
key: key,
data: valueOrSetter,
key: latestKey.current,
data: JSON.stringify(nextState),
})
setValue(valueOrSetter)
},
[key, value],
[],
)
const remove: ReturnType<typeof _useLocalStorage>[2] = useCallback(() => {
removeStorage({ key })
setValue(undefined)
}, [key])
return [value as any, set, remove]

const reset: UseLocalStorageResult<S | undefined>[2] = useCallback(() => {
setState(latestInitialState.current)
removeStorage({
key: latestKey.current,
})
}, [])

return [state, set, reset]
}
24 changes: 24 additions & 0 deletions src/react/useLocalStorage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { act, renderHook } from '@testing-library/react-hooks'
import { useLocalStorage } from './useLocalStorage'

describe('useLocalStorage', () => {
test('表现正常', async () => {
const { result: resx } = renderHook(() => useLocalStorage<string>('x'))
expect(resx.current[0]).toBe(undefined)
act(() => resx.current[1]('1'))
expect(resx.current[0]).toBe('1')
act(() => resx.current[1](prev => `${prev}_2`))
expect(resx.current[0]).toBe('1_2')
act(() => resx.current[2]())
expect(resx.current[0]).toBe(undefined)

const { result: resy } = renderHook(() => useLocalStorage('y', 5))
expect(resy.current[0]).toBe(5)
act(() => resy.current[1](1))
expect(resy.current[0]).toBe(1)
act(() => resy.current[1](prev => prev + 2))
expect(resy.current[0]).toBe(3)
act(() => resy.current[2]())
expect(resy.current[0]).toBe(5)
})
})
64 changes: 62 additions & 2 deletions src/react/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,62 @@
/* istanbul ignore file */
export { useLocalStorage } from 'react-use'
import { Dispatch, SetStateAction, useCallback, useState } from 'react'
import { useLatest, useUpdateEffect } from 'react-use'

export type UseLocalStorageResult<S> = readonly [
S,
Dispatch<SetStateAction<S>>,
() => void,
]

export function useLocalStorage<S>(
key: string,
): UseLocalStorageResult<S | undefined>

export function useLocalStorage<S>(
key: string,
initialState: S,
): UseLocalStorageResult<S>

export function useLocalStorage<S>(
key: string,
initialState?: S,
): UseLocalStorageResult<S | undefined> {
const getLocalStorageItem = useCallback(() => {
try {
const data = localStorage.getItem(key)
if (data != null) {
return JSON.parse(data)
}
return initialState
} catch {
return initialState
}
}, [key, initialState])

const [state, setState] = useState(getLocalStorageItem)

const latestKey = useLatest(key)
const latestInitialState = useLatest(initialState)
const latestState = useLatest(state)

useUpdateEffect(() => {
setState(getLocalStorageItem())
}, [key])

const set: UseLocalStorageResult<S | undefined>[1] = useCallback(
nextState => {
if (typeof nextState === 'function') {
nextState = (nextState as any)(latestState.current)
}
setState(nextState)
localStorage.setItem(latestKey.current, JSON.stringify(nextState))
},
[],
)

const reset: UseLocalStorageResult<S | undefined>[2] = useCallback(() => {
localStorage.removeItem(latestKey.current)
setState(latestInitialState.current)
}, [])

return [state, set, reset]
}

0 comments on commit 1d44804

Please sign in to comment.