-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: New hooks and server-side compatibility (#203)
- Loading branch information
Showing
26 changed files
with
586 additions
and
74 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,6 @@ flow-typed | |
[lints] | ||
|
||
[options] | ||
esproposal.optional_chaining=enable | ||
|
||
[strict] |
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
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 |
---|---|---|
@@ -1,11 +1,14 @@ | ||
// @flow | ||
|
||
export * from './useAutoFocus'; | ||
export * from './useClassName'; | ||
export * from './useDebouncedUpdates'; | ||
export * from './useDebouncedCallback'; | ||
export * from './useExclusiveBooleanProps'; | ||
export * from './useDebouncedValue'; | ||
export * from './useLazyRef'; | ||
export * from './useMediaQuery'; | ||
export * from './useMergedRefs'; | ||
export * from './useMutableCallback'; | ||
export * from './useSafely'; | ||
export * from './useToggle'; | ||
export * from './useUniqueId'; |
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,26 @@ | ||
// @flow | ||
|
||
import { useEffect, useRef } from 'react'; | ||
|
||
type FocusOptions = { | ||
preventScroll?: boolean, | ||
} | typeof undefined; | ||
|
||
/** | ||
* Hook to automatically request focus for an DOM element. | ||
* | ||
* @param isFocused if true, the focus will be requested | ||
* @param options options of the focus request | ||
* @return the ref which holds the element | ||
*/ | ||
export const useAutoFocus = (isFocused: boolean = true, options: FocusOptions) => { | ||
const elementRef = useRef<?HTMLElement>(); | ||
|
||
useEffect(() => { | ||
if (isFocused && elementRef.current) { | ||
elementRef.current.focus(options); | ||
} | ||
}, [elementRef, isFocused]); | ||
|
||
return elementRef; | ||
}; |
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,26 @@ | ||
// @flow | ||
|
||
import { useEffect, useState } from 'react'; | ||
|
||
/** | ||
* Hook to keep a debounced reference of a value. | ||
* | ||
* @param value the value to be debounced | ||
* @param delay the number of milliseconds to delay | ||
* @return a debounced value | ||
*/ | ||
export const useDebouncedValue = (value: any, delay: number) => { | ||
const [debouncedValue, setDebouncedValue] = useState(value); | ||
|
||
useEffect(() => { | ||
const timer = setTimeout(() => { | ||
setDebouncedValue(value); | ||
}, delay); | ||
|
||
return () => { | ||
clearTimeout(timer); | ||
}; | ||
}, [value, delay]); | ||
|
||
return debouncedValue; | ||
}; |
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,16 @@ | ||
// @flow | ||
|
||
import { createRef, useState } from 'react'; | ||
|
||
/** | ||
* Hook equivalent to useRef, but with a lazy initialization for computed value. | ||
* | ||
* @param initializer the function the computes the ref value | ||
* @return the ref | ||
*/ | ||
export const useLazyRef = <T>(initializer: () => T) => | ||
useState(() => { | ||
const ref = createRef<T>(); | ||
ref.current = initializer(); | ||
return ref; | ||
})[0]; |
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,36 @@ | ||
// @flow | ||
|
||
import { useEffect, useRef } from 'react'; | ||
|
||
import { useMutableCallback } from './useMutableCallback'; | ||
|
||
/** | ||
* Hook that wraps pairs of state and updater to provide a new updater which | ||
* can be safe and asynchronically called even after the component unmounted. | ||
* | ||
* @param pair - the state and updater pair which will be patched | ||
* @param pair.0 - the state value | ||
* @param pair.1 - the state updater function | ||
* @return a state value and safe updater pair | ||
*/ | ||
export const useSafely = ([state, updater]: [any, () => any]) => { | ||
const mountedRef = useRef(true); | ||
|
||
useEffect(() => { | ||
mountedRef.current = true; | ||
|
||
return () => { | ||
mountedRef.current = false; | ||
}; | ||
}); | ||
|
||
const safeUpdater = useMutableCallback((...args) => { | ||
if (!mountedRef.current) { | ||
return; | ||
} | ||
|
||
updater(...args); | ||
}); | ||
|
||
return [state, safeUpdater]; | ||
}; |
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,51 @@ | ||
import { useState } from 'react'; | ||
|
||
import { runHooks } from '../.jest/helpers'; | ||
import { useAutoFocus } from '../src'; | ||
|
||
describe('useAutoFocus hook', () => { | ||
it('returns a ref', () => { | ||
const [ref] = runHooks(() => useAutoFocus()); | ||
|
||
expect(ref).toMatchObject({ current: undefined }); | ||
}); | ||
|
||
it('invokes focus', () => { | ||
const focus = jest.fn(); | ||
runHooks(() => useAutoFocus(), [ | ||
(ref) => { | ||
ref.current = { focus }; | ||
}, | ||
]); | ||
|
||
expect(focus).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('does not invoke focus if isFocused is false', () => { | ||
const focus = jest.fn(); | ||
runHooks(() => useAutoFocus(false), [ | ||
(ref) => { | ||
ref.current = { focus }; | ||
}, | ||
]); | ||
|
||
expect(focus).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('invokes focus if isFocused is toggled', () => { | ||
const focus = jest.fn(); | ||
runHooks(() => { | ||
const [isFocused, setFocused] = useState(false); | ||
return [useAutoFocus(isFocused), setFocused]; | ||
}, [ | ||
([ref]) => { | ||
ref.current = { focus }; | ||
}, | ||
([, setFocused]) => { | ||
setFocused(true); | ||
}, | ||
]); | ||
|
||
expect(focus).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
Oops, something went wrong.