Skip to content

Add support for null values on some built-in equality checks #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion packages/@react-facet/core/src/createEqualityChecks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
import { EqualityCheck } from './types'
import { EqualityCheck, NO_VALUE, NoValue } from './types'

/**
* Creates an equality check that accepts null and undefined values
*
* @param comparator comparator to be wrapped with the null check
*/
export const createNullableEqualityCheck = <T>(comparator: EqualityCheck<T>) => {
const check = comparator()
let previous: T | null | undefined | NoValue = NO_VALUE

return (value: T | null | undefined) => {
if (value == null || previous == null) {
if (value != previous) {
previous = value
return false
} else {
return true
}
}

previous = value
return check(value)
}
}

/**
* Creates an equality check that tests that the values of all the properties in an object
Expand Down
57 changes: 56 additions & 1 deletion packages/@react-facet/core/src/equalityChecks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { shallowObjectEqualityCheck, strictEqualityCheck, defaultEqualityCheck } from './equalityChecks'
import {
shallowObjectEqualityCheck,
strictEqualityCheck,
defaultEqualityCheck,
nullableShallowObjectEqualityCheck,
} from './equalityChecks'

describe('shallowObjectEqualityCheck()', () => {
it('handles matching objects', () => {
Expand Down Expand Up @@ -36,6 +41,56 @@ describe('shallowObjectEqualityCheck()', () => {
})
})

describe('nullableShallowObjectEqualityCheck()', () => {
it('handles matching objects', () => {
const equalityCheck = nullableShallowObjectEqualityCheck()
expect(equalityCheck({})).toBe(false)
expect(equalityCheck({})).toBe(true)

expect(equalityCheck({ foo: 'bar' })).toBe(false)
expect(equalityCheck({ foo: 'bar' })).toBe(true)

expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(true)

expect(equalityCheck({})).toBe(false)
expect(equalityCheck({})).toBe(true)

expect(equalityCheck({ foo: 'bar' })).toBe(false)
expect(equalityCheck({ foo: 'bar' })).toBe(true)
})

it('handles non-matching objects', () => {
const equalityCheck = nullableShallowObjectEqualityCheck()

expect(equalityCheck({ foo: 1 })).toBe(false)
expect(equalityCheck({ foo: true })).toBe(false) // Make sure it does a strict type check

expect(equalityCheck({ foo: 'bar' })).toBe(false)
expect(equalityCheck({ foo: 'bars' })).toBe(false)

expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
expect(equalityCheck({ foo: 'baz', bar: undefined })).toBe(false)

expect(equalityCheck({ foo: 'bar', bar: 'baz' })).toBe(false)
expect(equalityCheck({ foo: 'bar' })).toBe(false)
})

it('handles null and undefined', () => {
const equalityCheck = nullableShallowObjectEqualityCheck()

expect(equalityCheck({ foo: 1 })).toBe(false)
expect(equalityCheck({ foo: 1 })).toBe(true)

expect(equalityCheck(null)).toBe(false)

expect(equalityCheck({ foo: 1 })).toBe(false)
expect(equalityCheck({ foo: 1 })).toBe(true)

expect(equalityCheck(undefined)).toBe(false)
})
})

describe('strictEqualityCheck', () => {
it('handles function comparisons by reference', () => {
const equalityCheck = strictEqualityCheck()
Expand Down
39 changes: 38 additions & 1 deletion packages/@react-facet/core/src/equalityChecks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { createUniformArrayEqualityCheck, createUniformObjectEqualityCheck } from './createEqualityChecks'
import {
createUniformArrayEqualityCheck,
createUniformObjectEqualityCheck,
createNullableEqualityCheck,
} from './createEqualityChecks'
import { ObjectWithImmutables, Immutable, Option, NO_VALUE } from './types'

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

/**
* Does a shallow object equality check for each element in an array
*
* For null or undefined values see nullableShallowObjectArrayEqualityCheck
*/
export const shallowObjectArrayEqualityCheck =
createUniformArrayEqualityCheck<ObjectWithImmutables>(shallowObjectEqualityCheck)

/**
* Shallow equality check of primitives in an array
*
* For null or undefined values see nullableShallowArrayEqualityCheck
*/
export const shallowArrayEqualityCheck = createUniformArrayEqualityCheck<Immutable>(strictEqualityCheck)

/**
* Equality check that verifies the values of each key of an object.
* Each value must be a primitive (boolean, number or string)
*
* Supports nullable values
*/
export const nullableShallowObjectEqualityCheck = () =>
createNullableEqualityCheck(createUniformObjectEqualityCheck<ObjectWithImmutables>(strictEqualityCheck))

/**
* Does a shallow object equality check for each element in an array
*
* Supports nullable values
*/
export const nullableShallowObjectArrayEqualityCheck = createNullableEqualityCheck(
createUniformArrayEqualityCheck<ObjectWithImmutables>(shallowObjectEqualityCheck),
)

/**
* Shallow equality check of primitives in an array
*
* Supports nullable values
*/
export const nullableShallowArrayEqualityCheck = createNullableEqualityCheck(
createUniformArrayEqualityCheck<Immutable>(strictEqualityCheck),
)

/**
* The default equality check that assumes data can be mutated.
* It is used internally by default, so there is no need to provide it.
Expand Down