Last active
January 30, 2019 21:45
-
-
Save yangshun/719f5390c28c0b7f7cd1b4e031526fad to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const {useCallback, useState} = require('React'); | |
const nullthrows = require('nullthrows'); | |
type InputValue<T> = {| | |
// Whether the input value has changed since it was initialized. | |
changed: boolean, | |
// A string key to uniquely identify the value. | |
key: ?string, | |
// Whether the input value is invalid. | |
invalid: boolean, | |
// The input value that's transformed for submission. Useful when working | |
// with an intermediate format that needs to be transformed before sending | |
// as a payload within a request. | |
submissionValue: mixed, | |
// Where the input value is valid. | |
valid: boolean, | |
// The raw input value. | |
value: T, | |
// Callback to update the input value. | |
onChange: (value: T) => void, | |
|}; | |
function useInputValue<T>( | |
initialValue: T, | |
opts?: {| | |
// Predicate to determine if two values are different. | |
changePredicate?: (currentValue: T, initialValue: T) => boolean, | |
// Unique key of the form value. Will be the key of the object if used in formInputValues. | |
key?: string, | |
// Transforms a value before being submitted. | |
submissionValueTransform?: (currentValue: T) => mixed, | |
// Determines if a value is valid. | |
validate?: (value: T) => boolean, | |
|}, | |
): InputValue<T> { | |
const [value, setValue] = useState(initialValue); | |
const onChange = useCallback(value => { | |
setValue(value); | |
}, []); | |
const options = Object.assign( | |
{}, | |
{ | |
// Defaults to reference equality. | |
changePredicate: (currentValue: T, initialValue: T) => | |
currentValue !== initialValue, | |
key: null, | |
// Defaults to identity. | |
submissionValueTransform: (currentValue: T) => currentValue, | |
// Defaults to true. | |
validate: (_: T) => true, | |
}, | |
opts, | |
); | |
const valid = options.validate(value); | |
return { | |
changed: options.changePredicate(value, initialValue), | |
key: options.key, | |
invalid: !valid, | |
submissionValue: options.submissionValueTransform(value), | |
value, | |
valid, | |
onChange, | |
}; | |
} | |
type FormInputValues = {| | |
// An object of all values, where the key is the field key. | |
allValues: {[string]: mixed}, | |
// An array of keys where values have changed. | |
changedKeys: Array<string>, | |
// Whether any value has changed. | |
hasChanged: boolean, | |
// Whether any value is invalid. | |
hasInvalid: boolean, | |
// An array of keys where their values are invalid. | |
invalidKeys: Array<string>, | |
// An object of values after mapping to a submission-friendly format. | |
submissionValues: {[string]: mixed}, | |
|}; | |
/** | |
* Receives an array of input values and determines the overall form validity state. | |
*/ | |
function formInputValues( | |
values: Array< | |
$IntentionallyInexact<{ | |
changed: boolean, | |
invalid: boolean, | |
key: ?string, | |
submissionValue: mixed, | |
}>, | |
>, | |
): FormInputValues { | |
const allValues = values | |
.filter(value => value.key) | |
.reduce((values, value) => { | |
values[nullthrows(value.key)] = value.submissionValue; | |
return values; | |
}, {}); | |
const changedKeys: Array<string> = values | |
.filter(value => value.changed && value.key) | |
.map(value => nullthrows(value.key)); | |
const invalidKeys: Array<string> = values | |
.filter(value => value.invalid) | |
.map(value => nullthrows(value.key)); | |
const submissionValues = values | |
.filter(value => value.changed && value.key) | |
.reduce((values, value) => { | |
values[nullthrows(value.key)] = value.submissionValue; | |
return values; | |
}, {}); | |
return { | |
allValues, | |
changedKeys, | |
hasChanged: changedKeys.length > 0, | |
hasInvalid: invalidKeys.length > 0, | |
invalidKeys, | |
submissionValues, | |
}; | |
} | |
module.exports = { | |
useInputValue, | |
formInputValues, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment