Skip to content

Commit 6c9913c

Browse files
Viktor Bergehallpirelenito
Viktor Bergehall
andauthored
Keep useFacetCallback instance same even if facets instances change (#119)
* WIP: Make sure we keep the same callback instance when useFacetCallback gets re-rendered * Added unit test * Updated unit test * Fixed import error --------- Co-authored-by: Paulo Ragonha <[email protected]>
1 parent 04f6fd5 commit 6c9913c

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

packages/@react-facet/core/src/hooks/useFacetCallback.spec.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { act, render } from '@react-facet/dom-fiber-testing-library'
33
import { useFacetCallback } from './useFacetCallback'
44
import { useFacetEffect } from './useFacetEffect'
55
import { useFacetMap } from './useFacetMap'
6-
import { NO_VALUE } from '../types'
7-
import { createFacet } from '../facet'
6+
import { NO_VALUE, Facet } from '../types'
7+
import { createFacet, createStaticFacet } from '../facet'
88
import { NoValue } from '..'
99

1010
it('captures the current value of the facet in a function that can be used as handler', () => {
@@ -321,4 +321,32 @@ describe('regressions', () => {
321321
expect(result).toBe('valuestring')
322322
})
323323
})
324+
325+
it('always returns the same callback instance, even if the Facet instances change', () => {
326+
let handler: () => void = () => {}
327+
const facetA = createStaticFacet('a')
328+
const facetB = createStaticFacet('b')
329+
330+
const TestComponent = ({ facet }: { facet: Facet<string> }) => {
331+
handler = useFacetCallback(
332+
(a) => () => {
333+
return a
334+
},
335+
[],
336+
[facet],
337+
)
338+
339+
return null
340+
}
341+
342+
const { rerender } = render(<TestComponent facet={facetA} />)
343+
const firstHandler = handler
344+
expect(firstHandler()).toBe('a')
345+
346+
rerender(<TestComponent facet={facetB} />)
347+
const secondHandler = handler
348+
expect(secondHandler()).toBe('b')
349+
350+
expect(firstHandler).toBe(secondHandler)
351+
})
324352
})

packages/@react-facet/core/src/hooks/useFacetCallback.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useLayoutEffect } from 'react'
1+
import { useCallback, useLayoutEffect, useRef } from 'react'
22
import { Facet, NO_VALUE, ExtractFacetValues, NoValue } from '../types'
33

44
/**
@@ -59,8 +59,14 @@ export function useFacetCallback<M, Y extends Facet<unknown>[], T extends [...Y]
5959
// eslint-disable-next-line react-hooks/exhaustive-deps
6060
const callbackMemoized = useCallback(callback, dependencies)
6161

62+
// Setup a ref so that the callback instance below can be kept the same
63+
// when Facet instances change across re-renders
64+
const facetsRef = useRef(facets)
65+
facetsRef.current = facets
66+
6267
return useCallback(
6368
(...args: K) => {
69+
const facets = facetsRef.current
6470
const values = facets.map((facet) => facet.get())
6571

6672
for (const value of values) {
@@ -69,8 +75,6 @@ export function useFacetCallback<M, Y extends Facet<unknown>[], T extends [...Y]
6975

7076
return callbackMemoized(...(values as ExtractFacetValues<T>))(...(args as K))
7177
},
72-
// We care about each individual facet and if any is a different reference
73-
// eslint-disable-next-line react-hooks/exhaustive-deps
74-
[callbackMemoized, defaultReturnValue, ...facets],
78+
[callbackMemoized, defaultReturnValue],
7579
)
7680
}

0 commit comments

Comments
 (0)