Skip to content

Commit

Permalink
feat(useControllableValue): 支持重置值
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Aug 6, 2020
1 parent 315cd2e commit 0ffb359
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 20 deletions.
24 changes: 24 additions & 0 deletions src/react/useControllableValue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,28 @@ describe('useControllableValue', () => {
act(() => result2.current.setValue('2'))
expect(result2.current.value).toBe('2')
})

test('reset', () => {
const { result } = renderHook(() => {
const [value, setValue, resetValue] = useControllableValue(
{ value: '1' } as Partial<{
value: string
defaultValue: string
onChange: (value: string) => any
}>,
{
defaultValuePropName: 'defaultValue',
valuePropName: 'value',
callbackPropName: 'onChange',
alwaysUpdateValue: true,
},
)
return { value, setValue, resetValue }
})
expect(result.current.value).toBe('1')
act(() => result.current.setValue('2'))
expect(result.current.value).toBe('2')
act(() => result.current.resetValue())
expect(result.current.value).toBe('1')
})
})
55 changes: 35 additions & 20 deletions src/react/useControllableValue.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Defined } from '../types'
import { useCallback, useState } from 'react'
import { useUpdateEffect } from 'react-use'
import { useLatest, useUpdateEffect } from 'react-use'

export type UseControllableValueOptions<
TProps,
Expand Down Expand Up @@ -45,6 +45,7 @@ export type UseControllableValueResult<
? TProps[TValuePropName]
: Defined<TProps[TValuePropName]>,
Defined<TProps[TCallbackPropName]>,
() => void,
]

/**
Expand Down Expand Up @@ -74,33 +75,47 @@ export function useControllableValue<
TCallbackPropName,
TDefaultValue
> {
const [value, setValue] = useState(() => {
if (options.valuePropName in props) {
return props[options.valuePropName]
const latestProps = useLatest(props)
const latestOptions = useLatest(options)

const getInitialValue = useCallback(() => {
if (latestOptions.current.valuePropName in latestProps.current) {
return latestProps.current[latestOptions.current.valuePropName]
}
if (options.defaultValuePropName in props) {
return props[options.defaultValuePropName]
if (latestOptions.current.defaultValuePropName in latestProps.current) {
return latestProps.current[latestOptions.current.defaultValuePropName]
}
return options.defaultValue
})
return latestOptions.current.defaultValue
}, [])

const [value, setValue] = useState(getInitialValue)

useUpdateEffect(() => {
if (options.valuePropName in props) {
setValue(props[options.valuePropName])
}
}, [props[options.valuePropName]])

const handleSetValue = useCallback(
(nextValue: typeof value) => {
if (!(options.valuePropName in props) || options.alwaysUpdateValue) {
setValue(nextValue)
}
if (typeof props[options.callbackPropName] === 'function') {
;(props[options.callbackPropName] as any)(nextValue)
}
},
[props, options.valuePropName, options.callbackPropName],
)
const handleSetValue = useCallback((nextValue: typeof value) => {
if (
!(latestOptions.current.valuePropName in latestProps.current) ||
latestOptions.current.alwaysUpdateValue
) {
setValue(nextValue)
}
if (
typeof latestProps.current[latestOptions.current.callbackPropName] ===
'function'
) {
;(latestProps.current[latestOptions.current.callbackPropName] as any)(
nextValue,
)
}
}, [])

const handleResetValue = useCallback(() => {
handleSetValue(getInitialValue())
}, [])

return [value, handleSetValue] as any
return [value, handleSetValue, handleResetValue] as any
}

0 comments on commit 0ffb359

Please sign in to comment.