react-bfm
Version:
A basic field / form manager for React using hooks
132 lines (121 loc) • 4.35 kB
text/typescript
import { createContext } from 'react'
import { defaultDirtyCheck, mapFieldValueAndError, validateFieldName, validateNamespace } from './helpers'
import {
getFieldState,
getNamespaceState,
initFieldState,
removeField,
StateCreatorReturnType,
createGetSnapshotFieldState,
createGetSnapshotNamespaceState,
createSubscribeToField,
createSubscribeToNamespace,
updateFieldStateWithCallback,
} from './state'
import { DirtyCheckFunction, FieldNameType, FieldStateType, NamespaceType } from './common'
import {
FIELD_KEY_INITIAL_VALUE,
FIELD_KEY_INITIAL_VALUE_ERROR,
FIELD_KEY_DIRTY,
FIELD_KEY_ERROR,
FIELD_KEY_FOCUS,
FIELD_KEY_TOUCHED,
FIELD_KEY_VALUE,
FIELD_KEY_VALUE_ON_FOCUS,
} from './constants/field-keys'
const focusField = (namespace: NamespaceType, fieldName: FieldNameType) => {
updateFieldStateWithCallback(namespace, fieldName, (currentState: FieldStateType) => ({
[FIELD_KEY_FOCUS]: true,
[FIELD_KEY_VALUE_ON_FOCUS]: currentState[FIELD_KEY_VALUE],
}))
}
const changeField = (
namespace: NamespaceType,
fieldName: FieldNameType,
value: any,
error: any,
dirtyCheck = defaultDirtyCheck,
) => {
updateFieldStateWithCallback(namespace, fieldName, (currentState: FieldStateType) => ({
[FIELD_KEY_DIRTY]: dirtyCheck(value, currentState[FIELD_KEY_VALUE_ON_FOCUS]),
...mapFieldValueAndError(value, error),
}))
}
const blurField = (namespace: NamespaceType, fieldName: FieldNameType) => {
updateFieldStateWithCallback(namespace, fieldName, () => ({
[FIELD_KEY_FOCUS]: false,
[FIELD_KEY_TOUCHED]: true,
[FIELD_KEY_VALUE_ON_FOCUS]: null,
}))
}
const initField = (namespace: NamespaceType, fieldName: FieldNameType, value: any, error: any) => {
if (process.env.NODE_ENV !== 'production') {
if (!validateNamespace(namespace)) {
throw new Error('Expected string with a minimal length of 1 for `namespace`')
}
if (!validateFieldName(fieldName)) {
throw new Error('Expected string with a minimal length of 1 for `fieldName`')
}
}
initFieldState(namespace, fieldName, value, error)
}
/**
* sets initial value only when the field is not touched and not focused
* this way you can still change the input value after first rendering
*/
const initialValueField = (namespace: NamespaceType, fieldName: FieldNameType, initialValue: any, error: any) =>
updateFieldStateWithCallback(namespace, fieldName, (currentState) => {
// only update value and error when field is not touched and not focused
const updateState: Partial<FieldStateType> =
!currentState[FIELD_KEY_TOUCHED] &&
!currentState[FIELD_KEY_FOCUS] &&
currentState[FIELD_KEY_VALUE] !== initialValue
? mapFieldValueAndError(initialValue, error)
: {}
// update error if value is still default
if (currentState[FIELD_KEY_VALUE] === initialValue && currentState[FIELD_KEY_ERROR] !== error) {
updateState.error = error
updateState.valid = !error
}
return {
...updateState,
[FIELD_KEY_INITIAL_VALUE]: initialValue,
[FIELD_KEY_INITIAL_VALUE_ERROR]: error,
}
})
/**
* @deprecated Will be removed in v3.0.0. This is an internal implementation detail and should not be used directly.
*/
export interface BFMHookContextType extends Omit<
StateCreatorReturnType,
'updateFieldStateWithCallback' | 'initFieldState'
> {
blurField: (namespace: NamespaceType, fieldName: FieldNameType) => void
changeField: (
namespace: NamespaceType,
fieldName: FieldNameType,
value: any,
error: any,
dirtyCheck?: DirtyCheckFunction,
) => void
initialValueField: (namespace: NamespaceType, fieldName: FieldNameType, initialValue: any, error: any) => void
focusField: (namespace: NamespaceType, fieldName: FieldNameType) => void
initField: (namespace: NamespaceType, fieldName: FieldNameType, value: any, error: any) => void
}
/**
* @deprecated Will be removed in v3.0.0. This is an internal implementation detail and should not be used directly.
*/
export const BFMHooksContext = createContext<BFMHookContextType>({
blurField,
changeField,
initialValueField,
focusField,
getFieldState,
getNamespaceState,
initField,
removeField,
createGetSnapshotFieldState,
createGetSnapshotNamespaceState,
createSubscribeToField,
createSubscribeToNamespace,
})