@olapat/react-useform
Version:
react hook useform controller
164 lines (145 loc) • 4.17 kB
text/typescript
import { useCallback, useReducer } from 'react'
import useCheckValidate from '../useForm/useCheckValidate'
import type { Rules, ErrorForm } from '../useForm/useForm'
export type ValuesList<ValuesListType> = {
values: ValuesListType,
rules: Rules<ValuesListType>,
errors: ErrorForm<ValuesListType>
}
type DispatchType = {
type: string,
payload: any
}
export interface Props<ValuesListType> {
initialValues: ValuesList<ValuesListType>[]
}
export type UseListType<ValuesListType> = {
values: ValuesList<ValuesListType>[],
addListItem: (init: ValuesList<ValuesListType>) => void,
changeListItem: (ix: number, value: ValuesList<ValuesListType>) => void,
removeListItem: (ix: number) => void,
validateListItem: () => boolean,
addManyListItem: (num: number, init: ValuesListType) => void,
setList: (arrayList: ValuesList<ValuesListType>[]) => void,
setRulesItem: (ix: number, rules: Rules<ValuesListType>) => void
}
function reducerValues<ValuesListType>(state: ValuesList<ValuesListType>[], action: DispatchType) {
switch (action.type) {
case 'ADD': {
const newState = [...state, action.payload]
return newState
}
case 'ADD_MANY': {
const newState = [...state, ...action.payload]
return newState
}
case 'CHANGE': {
const newState = [...state]
newState.splice(action.payload.ix, 1, action.payload.value)
return newState
}
case 'REMOVE': {
const newState = [...state]
newState.splice(action.payload.ix, 1)
return newState
}
case 'SET':
return action.payload
case 'SET_RULES_ITEM': {
const newState = [...state]
const prevState = newState[action.payload.ix]
const keys = Object.keys(action.payload.rules)
const newErrors: {[unit in string]: undefined} = {}
keys.forEach(key => {
newErrors[key] = undefined
});
newState.splice(action.payload.ix, 1, { ...prevState, rules: { ...prevState.rules, ...action.payload.rules }, errors: { ...prevState.errors, ...newErrors } })
return newState
}
case 'RESET':
return []
default:
return state
}
}
const useList = <ValuesListType extends { [key: string]: any } = {}>(props: Props<ValuesListType>): UseListType<ValuesListType> => {
const { initialValues } = props
const [values, dispatchValues] = useReducer(reducerValues, initialValues || [])
const checkValidate = useCheckValidate()
const addListItem = useCallback((init: ValuesList<ValuesListType>) => {
dispatchValues({
type: 'ADD',
payload: init
})
}, [])
const changeListItem = useCallback((ix: number, value: ValuesList<ValuesListType>) => {
dispatchValues({
type: 'CHANGE',
payload: {
ix, value
}
})
}, [])
const removeListItem = useCallback((ix: number) => {
dispatchValues({
type: 'REMOVE',
payload: {
ix
}
})
}, [])
const validateListItem = useCallback(() => {
let isValid = true
const valuesFlat = values.map(item => {
const errors = checkValidate(item.values, item.rules)
if (Object.keys(errors).length) {
isValid = false
}
return {
...item,
errors
}
})
dispatchValues({
type: 'SET',
payload: valuesFlat
})
return isValid
}, [values, checkValidate])
const addManyListItem = useCallback((num: number, init: ValuesList<ValuesListType>['values']) => {
const arrayNumber = []
for (let index = 1; index <= Number(num); index++) {
arrayNumber.push(init)
}
dispatchValues({
type: 'ADD_MANY',
payload: arrayNumber
})
}, [])
const setList = useCallback((arrayList: ValuesList<ValuesListType>[]) => {
dispatchValues({
type: 'SET',
payload: arrayList
})
}, [])
const setRulesItem = useCallback((ix: number, rules: ValuesList<ValuesListType>['rules']) => {
dispatchValues({
type: 'SET_RULES_ITEM',
payload: {
ix,
rules
}
})
}, [])
return {
values,
addListItem,
changeListItem,
removeListItem,
validateListItem,
addManyListItem,
setList,
setRulesItem
}
}
export default useList