Skip to content

Commit

Permalink
Refactor so that in never calls setState, unless needed
Browse files Browse the repository at this point in the history
  • Loading branch information
pirelenito committed Jul 25, 2024
1 parent f1bc2db commit dd53972
Showing 1 changed file with 38 additions and 35 deletions.
73 changes: 38 additions & 35 deletions packages/@react-facet/core/src/hooks/useFacetUnwrap.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -12,12 +12,15 @@ export function useFacetUnwrap<T extends Value>(
prop: FacetProp<T>,
equalityCheck: EqualityCheck<T> = defaultEqualityCheck,
): T | NoValue {
const previousStateRef = useRef<T | NoValue>(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(() => {
Expand All @@ -30,42 +33,42 @@ export function useFacetUnwrap<T extends Value>(
}

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])
Expand Down

0 comments on commit dd53972

Please sign in to comment.