-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import
useStableCallback
hook from Via
This hook is useful for wrapping callbacks to avoid unnecessary re-renders due to callback props changing. See hypothesis/via#1129.
- Loading branch information
1 parent
489f5e3
commit 941ccac
Showing
2 changed files
with
62 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// These tests use Preact directly, rather than via Enzyme, to simplify debugging. | ||
import { render } from 'preact'; | ||
|
||
import { useStableCallback } from '../use-stable-callback'; | ||
|
||
describe('useStableCallback', () => { | ||
let container; | ||
let stableCallbackValues; | ||
|
||
beforeEach(() => { | ||
container = document.createElement('div'); | ||
stableCallbackValues = []; | ||
}); | ||
|
||
function Widget({ callback }) { | ||
const stableCallback = useStableCallback(callback); | ||
stableCallbackValues.push(stableCallback); | ||
return <button onClick={stableCallback}>Test</button>; | ||
} | ||
|
||
it('returns a wrapper with a stable identity', () => { | ||
render(<Widget callback={() => {}} />, container); | ||
render(<Widget callback={() => {}} />, container); | ||
|
||
assert.equal(stableCallbackValues.length, 2); | ||
assert.equal(stableCallbackValues[0], stableCallbackValues[1]); | ||
}); | ||
|
||
it('returned wrapper forwards to the latest callback', () => { | ||
const stub = sinon.stub(); | ||
render(<Widget callback={() => {}} />, container); | ||
render(<Widget callback={stub} />, container); | ||
|
||
assert.equal(stableCallbackValues.length, 2); | ||
stableCallbackValues.at(-1)('foo', 'bar', 'baz'); | ||
|
||
assert.calledWith(stub, 'foo', 'bar', 'baz'); | ||
}); | ||
}); |
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,23 @@ | ||
import { useRef } from 'preact/hooks'; | ||
|
||
/** | ||
* Return a function which wraps a callback to give it a stable value. | ||
* | ||
* The wrapper has a stable value across renders, but always forwards to the | ||
* callback from the most recent render. This is useful if you want to use a | ||
* callback inside a `useEffect` or `useMemo` hook without re-running the effect | ||
* or re-computing the memoed value when the callback changes. | ||
*/ | ||
export function useStableCallback<R, A extends any[], F extends (...a: A) => R>( | ||
callback: F, | ||
): F { | ||
const wrapper = useRef({ | ||
callback, | ||
call: (...args: A) => wrapper.current.callback(...args), | ||
}); | ||
|
||
// On each render, save the last callback value. | ||
wrapper.current.callback = callback; | ||
|
||
return wrapper.current.call as F; | ||
} |