diff --git a/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts b/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts index 1c2fe9a..2194a05 100644 --- a/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts +++ b/packages/@react-facet/core/src/hooks/useFacetUnwrap.ts @@ -1,4 +1,4 @@ -import { useLayoutEffect, useState } from 'react' +import { useLayoutEffect, useRef, useState } from 'react' import { FacetProp, isFacet, Value, NoValue, EqualityCheck, NO_VALUE } from '../types' import { defaultEqualityCheck } from '../equalityChecks' @@ -12,12 +12,15 @@ export function useFacetUnwrap( prop: FacetProp, equalityCheck: EqualityCheck = defaultEqualityCheck, ): T | NoValue { + const previousStateRef = useRef(NO_VALUE) + const [state, setState] = useState<{ value: T | NoValue }>(() => { if (!isFacet(prop)) return { value: prop } - return { - value: prop.get(), - } + const value = prop.get() + previousStateRef.current = value + + return { value } }) useLayoutEffect(() => { @@ -30,42 +33,42 @@ export function useFacetUnwrap( } return prop.observe((value) => { - setState((previousState) => { - const { value: previousValue } = previousState + const previousValue = previousStateRef.current + previousStateRef.current = value - /** - * Performs this equality check locally to prevent triggering two consecutive renderings - * for facets that have immutable values (unfortunately we can't handle mutable values). - * - * The two renderings might happen for the same state value if the Facet has a value on mount. - * - * The unwrap will get the value: - * - Once on initialization of the useState above - * - And another time on this observe initialization - */ - if (equalityCheck === defaultEqualityCheck) { - 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). + * + * The two renderings might happen for the same state value if the Facet has a value on mount. + * + * The unwrap will get the value: + * - Once on initialization of the useState above + * - And another time on this observe initialization + */ + 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 ( + (typeofValue === 'number' || + typeofValue === 'string' || + typeofValue === 'boolean' || + value === undefined || + value === null) && + value === previousValue + ) { + return } - if (previousValue !== NO_VALUE && isEqual(value)) { - return previousState - } + setState({ value }) + return + } + + if (previousValue !== NO_VALUE && isEqual(value)) { + return + } - return { value } - }) + setState({ value }) }) } }, [prop, equalityCheck])