Skip to content

Commit 6df2b6c

Browse files
authored
Add support for null values on some built-in equality checks (#15)
Extends a few of the built-in equality checks to also accept `null` and `undefined` as values. It is not uncommon for selectors or maps to have "optional" return values. So having it supported in the built-in checks makes them available to be used in more situations. Added new the checks: - `nullableShallowObjectEqualityCheck` - `nullableShallowObjectArrayEqualityCheck` - `nullableShallowArrayEqualityCheck`
1 parent 75b1876 commit 6df2b6c

File tree

3 files changed

+119
-3
lines changed

3 files changed

+119
-3
lines changed

packages/@react-facet/core/src/createEqualityChecks.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
1-
import { EqualityCheck } from './types'
1+
import { EqualityCheck, NO_VALUE, NoValue } from './types'
2+
3+
/**
4+
* Creates an equality check that accepts null and undefined values
5+
*
6+
* @param comparator comparator to be wrapped with the null check
7+
*/
8+
export const createNullableEqualityCheck = <T>(comparator: EqualityCheck<T>) => {
9+
const check = comparator()
10+
let previous: T | null | undefined | NoValue = NO_VALUE
11+
12+
return (value: T | null | undefined) => {
13+
if (value == null || previous == null) {
14+
if (value != previous) {
15+
previous = value
16+
return false
17+
} else {
18+
return true
19+
}
20+
}
21+
22+
previous = value
23+
return check(value)
24+
}
25+
}
226

327
/**
428
* Creates an equality check that tests that the values of all the properties in an object

packages/@react-facet/core/src/equalityChecks.spec.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { shallowObjectEqualityCheck, strictEqualityCheck, defaultEqualityCheck } from './equalityChecks'
1+
import {
2+
shallowObjectEqualityCheck,
3+
strictEqualityCheck,
4+
defaultEqualityCheck,
5+
nullableShallowObjectEqualityCheck,
6+
} from './equalityChecks'
27

38
describe('shallowObjectEqualityCheck()', () => {
49
it('handles matching objects', () => {
@@ -36,6 +41,56 @@ describe('shallowObjectEqualityCheck()', () => {
3641
})
3742
})
3843

44+
describe('nullableShallowObjectEqualityCheck()', () => {
45+
it('handles matching objects', () => {
46+
const equalityCheck = nullableShallowObjectEqualityCheck()
47+
expect(equalityCheck({})).toBe(false)
48+
expect(equalityCheck({})).toBe(true)
49+
50+
expect(equalityCheck({ foo: 'bar' })).toBe(false)
51+
expect(equalityCheck({ foo: 'bar' })).toBe(true)
52+
53+
expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
54+
expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(true)
55+
56+
expect(equalityCheck({})).toBe(false)
57+
expect(equalityCheck({})).toBe(true)
58+
59+
expect(equalityCheck({ foo: 'bar' })).toBe(false)
60+
expect(equalityCheck({ foo: 'bar' })).toBe(true)
61+
})
62+
63+
it('handles non-matching objects', () => {
64+
const equalityCheck = nullableShallowObjectEqualityCheck()
65+
66+
expect(equalityCheck({ foo: 1 })).toBe(false)
67+
expect(equalityCheck({ foo: true })).toBe(false) // Make sure it does a strict type check
68+
69+
expect(equalityCheck({ foo: 'bar' })).toBe(false)
70+
expect(equalityCheck({ foo: 'bars' })).toBe(false)
71+
72+
expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
73+
expect(equalityCheck({ foo: 'baz', bar: undefined })).toBe(false)
74+
75+
expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
76+
expect(equalityCheck({ foo: 'bar' })).toBe(false)
77+
})
78+
79+
it('handles null and undefined', () => {
80+
const equalityCheck = nullableShallowObjectEqualityCheck()
81+
82+
expect(equalityCheck({ foo: 1 })).toBe(false)
83+
expect(equalityCheck({ foo: 1 })).toBe(true)
84+
85+
expect(equalityCheck(null)).toBe(false)
86+
87+
expect(equalityCheck({ foo: 1 })).toBe(false)
88+
expect(equalityCheck({ foo: 1 })).toBe(true)
89+
90+
expect(equalityCheck(undefined)).toBe(false)
91+
})
92+
})
93+
3994
describe('strictEqualityCheck', () => {
4095
it('handles function comparisons by reference', () => {
4196
const equalityCheck = strictEqualityCheck()

packages/@react-facet/core/src/equalityChecks.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { createUniformArrayEqualityCheck, createUniformObjectEqualityCheck } from './createEqualityChecks'
1+
import {
2+
createUniformArrayEqualityCheck,
3+
createUniformObjectEqualityCheck,
4+
createNullableEqualityCheck,
5+
} from './createEqualityChecks'
26
import { ObjectWithImmutables, Immutable, Option, NO_VALUE } from './types'
37

48
/**
@@ -21,20 +25,53 @@ export const strictEqualityCheck = <T extends Immutable | Function>() => {
2125
/**
2226
* Equality check that verifies the values of each key of an object.
2327
* Each value must be a primitive (boolean, number or string)
28+
*
29+
* For null or undefined values see nullableShallowObjectEqualityCheck
2430
*/
2531
export const shallowObjectEqualityCheck = createUniformObjectEqualityCheck<ObjectWithImmutables>(strictEqualityCheck)
2632

2733
/**
2834
* Does a shallow object equality check for each element in an array
35+
*
36+
* For null or undefined values see nullableShallowObjectArrayEqualityCheck
2937
*/
3038
export const shallowObjectArrayEqualityCheck =
3139
createUniformArrayEqualityCheck<ObjectWithImmutables>(shallowObjectEqualityCheck)
3240

3341
/**
3442
* Shallow equality check of primitives in an array
43+
*
44+
* For null or undefined values see nullableShallowArrayEqualityCheck
3545
*/
3646
export const shallowArrayEqualityCheck = createUniformArrayEqualityCheck<Immutable>(strictEqualityCheck)
3747

48+
/**
49+
* Equality check that verifies the values of each key of an object.
50+
* Each value must be a primitive (boolean, number or string)
51+
*
52+
* Supports nullable values
53+
*/
54+
export const nullableShallowObjectEqualityCheck = () =>
55+
createNullableEqualityCheck(createUniformObjectEqualityCheck<ObjectWithImmutables>(strictEqualityCheck))
56+
57+
/**
58+
* Does a shallow object equality check for each element in an array
59+
*
60+
* Supports nullable values
61+
*/
62+
export const nullableShallowObjectArrayEqualityCheck = createNullableEqualityCheck(
63+
createUniformArrayEqualityCheck<ObjectWithImmutables>(shallowObjectEqualityCheck),
64+
)
65+
66+
/**
67+
* Shallow equality check of primitives in an array
68+
*
69+
* Supports nullable values
70+
*/
71+
export const nullableShallowArrayEqualityCheck = createNullableEqualityCheck(
72+
createUniformArrayEqualityCheck<Immutable>(strictEqualityCheck),
73+
)
74+
3875
/**
3976
* The default equality check that assumes data can be mutated.
4077
* It is used internally by default, so there is no need to provide it.

0 commit comments

Comments
 (0)