diff --git a/packages/@react-facet/core/src/hooks/useFacetUnwrap.spec.tsx b/packages/@react-facet/core/src/hooks/useFacetUnwrap.spec.tsx
index 8975a0c1..ec734aa2 100644
--- a/packages/@react-facet/core/src/hooks/useFacetUnwrap.spec.tsx
+++ b/packages/@react-facet/core/src/hooks/useFacetUnwrap.spec.tsx
@@ -217,3 +217,45 @@ it('does not trigger a re-render when changing a facet from undefined to undefin
expect(renderedMock).toHaveBeenCalledTimes(0)
})
+
+it('supports custom equality checks', () => {
+ const value = {}
+ const demoFacet = createFacet({ initialValue: value })
+
+ // Dummy equality check that always returns its not equal
+ const check = jest.fn().mockReturnValue(false)
+ const equalityCheck = jest.fn().mockReturnValue(check)
+
+ const renderedMock = jest.fn()
+
+ const ComponentWithFacetEffect = () => {
+ useFacetUnwrap(demoFacet, equalityCheck)
+ renderedMock()
+ return null
+ }
+
+ render()
+
+ // initialize equality checks once
+ expect(equalityCheck).toHaveBeenCalledTimes(1)
+
+ // but check for it twice, once upon initialization, then another on the first observed value
+ expect(check).toHaveBeenCalledTimes(2)
+ expect(check).toHaveBeenNthCalledWith(1, value)
+ expect(check).toHaveBeenNthCalledWith(2, value)
+
+ // as the custom equality check always returns false, we render twice on mount
+ expect(renderedMock).toHaveBeenCalledTimes(2)
+
+ jest.clearAllMocks()
+
+ // If we update with the same object,
+ act(() => {
+ demoFacet.set(value)
+ })
+
+ expect(equalityCheck).toHaveBeenCalledTimes(0) // equality check was already initialized
+ expect(check).toHaveBeenCalledTimes(1) // but the check should be executed
+ expect(check).toHaveBeenCalledWith(value) // passing the value (which should be the same)
+ expect(renderedMock).toHaveBeenCalledTimes(1) // and since the equality check always returns "false", we have a render
+})
diff --git a/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts b/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts
index 53c3ad68..c2bba377 100644
--- a/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts
+++ b/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts
@@ -1,5 +1,6 @@
import { useLayoutEffect, useState } from 'react'
-import { FacetProp, isFacet, Value, NoValue } from '../types'
+import { FacetProp, isFacet, Value, NoValue, EqualityCheck, NO_VALUE } from '../types'
+import { defaultEqualityCheck } from '../equalityChecks'
/**
* Hook that allows consuming values from a Facet
@@ -7,7 +8,10 @@ import { FacetProp, isFacet, Value, NoValue } from '../types'
*
* @param facet
*/
-export function useFacetUnwrap(prop: FacetProp): T | NoValue {
+export function useFacetUnwrap(
+ prop: FacetProp,
+ equalityCheck: EqualityCheck = defaultEqualityCheck,
+): T | NoValue {
const [state, setState] = useState<{ value: T | NoValue }>(() => {
if (!isFacet(prop)) return { value: prop }
@@ -18,12 +22,17 @@ export function useFacetUnwrap(prop: FacetProp): T | NoValue
useLayoutEffect(() => {
if (isFacet(prop)) {
+ // Initialize the equalityCheck
+ const isEqual = equalityCheck()
+ const startValue = prop.get()
+ if (startValue !== NO_VALUE) {
+ isEqual(startValue)
+ }
+
return prop.observe((value) => {
setState((previousState) => {
const { value: previousValue } = previousState
- const typeofValue = typeof previousValue
-
/**
* Performs this equality check locally to prevent triggering two consecutive renderings
* for facets that have immutable values (unfortunately we can't handle mutable values).
@@ -34,14 +43,24 @@ export function useFacetUnwrap(prop: FacetProp): T | NoValue
* - Once on initialization of the useState above
* - And another time on this observe initialization
*/
- if (
- (typeofValue === 'number' ||
- typeofValue === 'string' ||
- typeofValue === 'boolean' ||
- value === undefined ||
- value === null) &&
- value === previousValue
- ) {
+ if (equalityCheck === defaultEqualityCheck) {
+ const typeofValue = typeof previousValue
+
+ if (
+ (typeofValue === 'number' ||
+ typeofValue === 'string' ||
+ typeofValue === 'boolean' ||
+ value === undefined ||
+ value === null) &&
+ value === previousValue
+ ) {
+ return previousState
+ }
+
+ return { value }
+ }
+
+ if (previousValue !== NO_VALUE && isEqual(previousValue)) {
return previousState
}
@@ -49,7 +68,7 @@ export function useFacetUnwrap(prop: FacetProp): T | NoValue
})
})
}
- }, [prop])
+ }, [prop, equalityCheck])
return isFacet(prop) ? state.value : prop
}