createOptionalValueEqualityCheck
Creates an equality check that handles optional/nullable values by wrapping an existing equality check. Useful for values that may be null
or undefined
.
When to Use
Use when you have an equality check for a type but need to handle nullable variants:
- ✅ Wrapping any equality check to handle
null
orundefined
- ✅ Optional properties that may not always be present
- ✅ Values that transition between defined and undefined states
- ❌ Simple primitives (use nullable variants like
nullableShallowArrayEqualityCheck
)
Signature
typescript
function createOptionalValueEqualityCheck<T>(valueEqualityCheck: EqualityCheck<T>): EqualityCheck<T | null | undefined>
Parameters:
valueEqualityCheck
- Equality check for the non-null type
Returns: An equality check that handles null
and undefined
, delegating to the provided check for defined values.
When using createOptionalValueEqualityCheck
with useFacetMap
or useFacetMemo
, you must create a stable reference to the equality check. Creating it inline on every render causes the facet to be recreated and internal state to be lost.
tsx
// ❌ WRONG - Creates new equality check on every renderconst Component = () => {const result = useFacetMap((data) => transform(data),[],[dataFacet],createOptionalValueEqualityCheck(shallowArrayEqualityCheck), // New reference every render!)}// ✅ CORRECT - Define outside componentconst optionalArrayCheck = createOptionalValueEqualityCheck(shallowArrayEqualityCheck)const Component = () => {const result = useFacetMap((data) => transform(data), [], [dataFacet], optionalArrayCheck)}// ✅ ALSO CORRECT - Use useMemoconst Component = () => {const equalityCheck = useMemo(() => createOptionalValueEqualityCheck(shallowArrayEqualityCheck), [])const result = useFacetMap((data) => transform(data), [], [dataFacet], equalityCheck)}
See Custom Equality Checks for more details.
Basic Usage
tsx
import {createOptionalValueEqualityCheck ,shallowArrayEqualityCheck } from '@react-facet/core'constequalityCheck =createOptionalValueEqualityCheck (shallowArrayEqualityCheck )()equalityCheck ([1, 2, 3])console .log (equalityCheck ([1, 2, 3])) // true - same arrayconsole .log (equalityCheck (null)) // false - changed to nullconsole .log (equalityCheck (null)) // true - still nullconsole .log (equalityCheck ([4, 5])) // false - back to defined valueconsole .log (equalityCheck ([4, 5])) // true - same array
Usage with Facets
Optional User Selection
tsx
import {useFacetMap ,useFacetWrap ,createOptionalValueEqualityCheck ,shallowObjectEqualityCheck ,} from '@react-facet/core'typeUser = {id : number;name : string }constUserProfile = () => {constselectedUserFacet =useFacetWrap <User | null>({id : 1,name : 'Steve' })constuserDisplayFacet =useFacetMap ((user ) => (user ? {displayName :user .name .toUpperCase (),userId :user .id } : null),[],[selectedUserFacet ],createOptionalValueEqualityCheck (shallowObjectEqualityCheck ),)return <div >User profile</div >}
Conditional Data Loading
tsx
import {useFacetMap ,useFacetWrap ,createOptionalValueEqualityCheck ,shallowArrayEqualityCheck ,} from '@react-facet/core'constDataList = () => {constdataFacet =useFacetWrap <number[] | null>([1, 2, 3])constprocessedDataFacet =useFacetMap ((data ) => (data ?data .map ((n ) =>n * 2) : null),[],[dataFacet ],createOptionalValueEqualityCheck (shallowArrayEqualityCheck ),)return <div >Data list</div >}
Form Field with Optional Value
tsx
import {useFacetMap ,useFacetWrap ,createOptionalValueEqualityCheck ,strictEqualityCheck } from '@react-facet/core'constFormField = () => {constinputFacet =useFacetWrap <string>('')// Parse to number or null if invalidconstnumberValueFacet =useFacetMap ((input ) => {constparsed =parseInt (input )returnisNaN (parsed ) ? null :parsed },[],[inputFacet ],createOptionalValueEqualityCheck (strictEqualityCheck ),)return <div >Form field</div >}
How It Works
The equality check handles three cases:
- Both null/undefined - Returns
true
(considered equal) - One null/undefined, one defined - Returns
false
(different) - Both defined - Delegates to the wrapped equality check
tsx
import {createOptionalValueEqualityCheck ,shallowArrayEqualityCheck } from '@react-facet/core'constcheck =createOptionalValueEqualityCheck (shallowArrayEqualityCheck )()check (null)console .log (check (null)) // true - both null ✅console .log (check (undefined )) // false - null ≠ undefined ❌console .log (check (undefined )) // true - both undefined ✅console .log (check ([5])) // false - undefined ≠ [5] ❌console .log (check ([5])) // true - both [5] ✅console .log (check ([10])) // false - [5] ≠ [10] ❌
null vs undefined
The check treats null
and undefined
as different values:
tsx
import {createOptionalValueEqualityCheck ,shallowArrayEqualityCheck } from '@react-facet/core'constcheck =createOptionalValueEqualityCheck (shallowArrayEqualityCheck )()check (null)console .log (check (null)) // true ✅console .log (check (undefined )) // false - null ≠ undefined ❌check (undefined )console .log (check (undefined )) // true ✅
If you need to treat them as equivalent, create a custom equality check.
Combining with Other Checks
With Object Equality
tsx
import {createOptionalValueEqualityCheck ,shallowObjectEqualityCheck } from '@react-facet/core'typeConfig = {theme : string;language : string }constcheck =createOptionalValueEqualityCheck (shallowObjectEqualityCheck )()check (null)console .log (check (null)) // true ✅console .log (check ({theme : 'dark',language : 'en' })) // false - null → defined ❌console .log (check ({theme : 'dark',language : 'en' })) // true - same object values ✅
With Array Equality
tsx
import {createOptionalValueEqualityCheck ,shallowArrayEqualityCheck } from '@react-facet/core'constcheck =createOptionalValueEqualityCheck (shallowArrayEqualityCheck )()check ([1, 2, 3])console .log (check ([1, 2, 3])) // true ✅console .log (check (null)) // false - defined → null ❌console .log (check (null)) // true ✅
With Nested Checks
tsx
import {createOptionalValueEqualityCheck ,createUniformArrayEqualityCheck ,shallowArrayEqualityCheck ,} from '@react-facet/core'// Optional 2D arrayconstcheck =createOptionalValueEqualityCheck (createUniformArrayEqualityCheck (shallowArrayEqualityCheck ))()check (null)console .log (check (null)) // true ✅console .log (check ([[1, 2],[3, 4],]),) // false - null → defined ❌console .log (check ([[1, 2],[3, 4],]),) // true - same nested arrays ✅
Important Notes
First Call Behavior
Like all equality checks, the first call always returns false
:
tsx
import {createOptionalValueEqualityCheck ,shallowArrayEqualityCheck } from '@react-facet/core'constcheck =createOptionalValueEqualityCheck (shallowArrayEqualityCheck )()console .log (check ([5])) // false - first call ❌console .log (check ([5])) // true - subsequent call with same value ✅
Null vs Undefined Distinction
Be explicit about which you're using:
tsx
// ✅ Explicit typetype MaybeUser = User | null // Clear: null indicates "no user selected"// ⚠️ Mixed - can be confusingtype MaybeUser = User | null | undefined // When is it null vs undefined?
Common Patterns
Search Results
tsx
import { useFacetMap, createOptionalValueEqualityCheck, shallowArrayEqualityCheck } from '@react-facet/core'const searchResultsFacet = useFacetMap((query, items) => {if (query.length === 0) return null // No search activereturn items.filter((item) => item.name.includes(query))},[],[queryFacet, itemsFacet],createOptionalValueEqualityCheck(shallowArrayEqualityCheck),)
Selected Item
tsx
import { useFacetMap, createOptionalValueEqualityCheck, shallowObjectEqualityCheck } from '@react-facet/core'const selectedItemFacet = useFacetMap((items, selectedId) => {if (selectedId === null) return nullreturn items.find((item) => item.id === selectedId) || null},[],[itemsFacet, selectedIdFacet],createOptionalValueEqualityCheck(shallowObjectEqualityCheck),)
Loaded Data
tsx
import { useFacetMap, createOptionalValueEqualityCheck, shallowArrayEqualityCheck } from '@react-facet/core'const dataFacet = useFacetMap((isLoading, rawData) => {if (isLoading) return null // Still loadingreturn rawData.map((item) => item.id)},[],[isLoadingFacet, rawDataFacet],createOptionalValueEqualityCheck(shallowArrayEqualityCheck),)
Alternative: Nullable Variants
For common types, use the built-in nullable variants instead:
tsx
import {nullableShallowArrayEqualityCheck,nullableShallowObjectEqualityCheck,nullableShallowObjectArrayEqualityCheck,} from '@react-facet/core'// ✅ Use built-in nullable variants when availableconst check1 = nullableShallowArrayEqualityCheck()// ✅ Use createOptionalValueEqualityCheck for custom typesconst check2 = createOptionalValueEqualityCheck(myCustomEqualityCheck)()
See Also
- Equality Checks Overview - Guide to all equality checks
shallowArrayEqualityCheck
- Array equality (has nullable variant)shallowObjectEqualityCheck
- Object equality (has nullable variant)- Custom Equality Checks - Create your own equality checks