diff --git a/dist/react-formutil.cjs.development.js b/dist/react-formutil.cjs.development.js index 86f68dc..a59fca8 100644 --- a/dist/react-formutil.cjs.development.js +++ b/dist/react-formutil.cjs.development.js @@ -388,6 +388,27 @@ function parsePath() { return target; } } +function pathExist(scope, path) { + var pathWords = path.split(PATH_REGEXP).map(function (s) { + return s.trim(); + }).filter(function (item) { + return item !== ''; + }); + + for (var index = 0, len = pathWords.length; index < len; index++) { + var word = executeWord(pathWords[index]); + + if (word in scope) { + if (index + 1 === len) { + return { + data: scope[word] + }; + } + + scope = scope[word]; + } + } +} function createRef(ref, value) { if (ref) { if (isFunction(ref)) { @@ -738,10 +759,10 @@ function (_Component) { var $parsedTree = _this.$$deepParseObject($stateTree); objectEach(_this.$$registers, function (handler, name) { - var data = name in $stateTree ? $stateTree[name] : parsePath($parsedTree, name); + var pathData; - if (!isUndefined(data) || force) { - var $newState = processer(data, handler); + if (force || (pathData = pathExist($parsedTree, name))) { + var $newState = processer(pathData && pathData.data, handler); if ($newState) { var $prevValue = _this.$formutil.$weakParams[name]; diff --git a/dist/react-formutil.cjs.development.js.map b/dist/react-formutil.cjs.development.js.map index 4fe18a0..9a6b2e0 100644 --- a/dist/react-formutil.cjs.development.js.map +++ b/dist/react-formutil.cjs.development.js.map @@ -1 +1 @@ -{"version":3,"file":"react-formutil.cjs.development.js","sources":["../src/context.js","../src/utils.js","../src/Form.js","../src/withForm.js","../src/fieldHelper.js","../src/Field.js","../src/withField.js","../src/EasyField/Native.js","../src/EasyField/Group.js","../src/EasyField/List.js","../src/EasyField/easyFieldHandler.js","../src/EasyField/index.js","../src/connect.js","../src/hooks/useFormContext.js","../src/hooks/useField.js","../src/hooks/useForm.js","../src/hooks/useHandler.js"],"sourcesContent":["import { createContext } from 'react';\n\nexport default createContext({});\n","import { isValidElementType } from 'react-is';\nimport warning from 'warning';\n\nconst OBJECT_PROTO = Object.getPrototypeOf({});\nconst PATH_REGEXP = /\\s*(?:\\]\\s*\\.|\\]\\s*\\[|\\.|\\[|\\])\\s*/g;\nconst Root = isUndefined(window) ? global : window;\n\nexport function isUndefined(arg) {\n return typeof arg === 'undefined';\n}\n\nexport function isFunction(arg) {\n return typeof arg === 'function';\n}\n\nexport function isEmpty(arg) {\n return isUndefined(arg) || arg === null || arg + '' === '';\n}\n\nexport function isPromise(promise) {\n return !!promise && isFunction(promise.then);\n}\n\nexport function isObject(obj) {\n return Object.prototype.toString.call(obj) === '[object Object]';\n}\n\nexport function isPlainObj(obj) {\n if (!isObject(obj)) return false;\n if (null === Object.getPrototypeOf(obj)) return true;\n if (!isFunction(obj.constructor)) return false;\n\n return obj.constructor.prototype === OBJECT_PROTO;\n}\n\nexport function isComponent(obj) {\n return isValidElementType(obj) && typeof obj !== 'string';\n}\n\nexport function checkComponentPropType(props, propName, componentName) {\n if (props[propName] && !isComponent(props[propName])) {\n return new Error(\n `Invalid prop 'component' supplied to '${componentName}': the prop is not a valid React component`\n );\n }\n}\n\n// quick clone deeply\nexport function deepClone(obj) {\n if (Array.isArray(obj)) {\n const newObj = [];\n\n for (let i = 0, j = obj.length; i < j; i++) {\n newObj[i] = deepClone(obj[i]);\n }\n\n return newObj;\n } else if (isPlainObj(obj)) {\n const newObj = {};\n\n for (let i in obj) {\n newObj[i] = deepClone(obj[i]);\n }\n\n return newObj;\n }\n\n return obj;\n}\n\nexport const runCallback = function(callback, ...args) {\n if (isFunction(callback)) {\n callback(...args);\n }\n\n return args[0];\n};\n\nexport function createHOC(withHOC) {\n return function(...args) {\n if (isComponent(args[0])) {\n return withHOC(...args);\n }\n\n return function(WrappedComponent) {\n return withHOC(WrappedComponent, args[0]);\n };\n };\n}\n\nconst VALID_PROPS = ['minlength', 'maxlength', 'max', 'min', 'required', 'pattern', 'step'];\nexport function isValidProp(prop) {\n return VALID_PROPS.indexOf(prop.toLowerCase()) > -1;\n}\n\n/* eslint-disable */\nconst executeWord = function(word) {\n try {\n const exec = new Function(\n 'origin',\n 'global',\n `return typeof ${word} === 'number' || (typeof ${word} !== 'undefined' && !(origin in global)) ? ${word} : origin`\n );\n return exec(word, Root);\n } catch (err) {\n return word;\n }\n};\n\n/**\n * @desc 解析表达式中赋值深路径对象\n *\n * @param {object} target 要赋值的对象\n * @param {string} path 赋值路径,eg:list[0].title\n * @param {any} [value] 要赋过去的值,如过不传,则返回解析路径后的值\n *\n * 使用示例:parsePath({}, 'list[0].authors[1].name', 'Lucy');\n */\nexport function parsePath(...args) {\n const [target, path, value] = args;\n\n warning(typeof path === 'string', `The second parameter(${JSON.stringify(path)}) of parsePath() must be a string.`);\n\n const pathSymbols = (path.match(PATH_REGEXP) || []).map(s => s.replace(/\\s/g, ''));\n const pathWords = path\n .split(PATH_REGEXP)\n .map(s => s.trim())\n .filter(item => item !== '');\n let scope = target;\n\n try {\n if (args.length < 3) {\n for (let index = 0, len = pathWords.length; index < len; index++) {\n const word = executeWord(pathWords[index]);\n\n if (index + 1 === len) {\n return scope[word];\n }\n\n if (isUndefined(scope[word])) {\n break;\n }\n\n scope = scope[word];\n }\n } else {\n for (let index = 0, length = pathWords.length; index < length; index++) {\n const word = executeWord(pathWords[index]);\n const nextWord = pathWords[index + 1];\n const symbol = pathSymbols[index];\n\n if (isUndefined(nextWord)) {\n scope[word] = value;\n break;\n }\n\n switch (symbol) {\n case '].':\n case '.':\n scope = isUndefined(scope[word]) ? (scope[word] = {}) : scope[word];\n break;\n\n case '][':\n case '[':\n const nextVarWord = executeWord(nextWord);\n\n scope = isUndefined(scope[word])\n ? (scope[word] = typeof nextVarWord === 'number' && nextVarWord >= 0 ? [] : {})\n : scope[word];\n break;\n\n default:\n scope[word] = value;\n break;\n }\n }\n }\n } catch (error) {\n warning(false, `The name '%s' of Field seems is not a legal expression.`, path);\n }\n\n if (args.length > 2) {\n return target;\n }\n}\n\nexport function createRef(ref, value) {\n if (ref) {\n if (isFunction(ref)) {\n ref(value);\n } else if ('current' in ref) {\n ref.current = value;\n }\n }\n}\n\nexport const arrayFind = (array, process) => {\n for (let i = 0, j = array.length; i < j; i++) {\n if (process(array[i]) === true) {\n return array[i];\n }\n }\n};\n\nexport const objectMap = (obj, handler) =>\n Object.keys(obj).reduce((newObj, key) => {\n newObj[key] = handler(obj[key], key, obj);\n return newObj;\n }, {});\n\nexport const objectEach = (obj, handler) => Object.keys(obj).forEach(key => handler(obj[key], key, obj));\n\nexport const toObject = (arr, handler, obj = {}) =>\n arr.reduce((...args) => {\n handler(...args);\n\n return args[0];\n }, obj);\n\nconst TODO_DELETE = undefined;\nexport function CLEAR(obj, pkey, pobj) {\n objectEach(obj, (value, key) => {\n if (value === TODO_DELETE) {\n delete obj[key];\n } else if (isPlainObj(value) || Array.isArray(value)) {\n CLEAR(value, key, obj);\n }\n });\n\n if (pobj && Object.keys(obj).every(key => obj[key] === TODO_DELETE)) {\n pobj[pkey] = TODO_DELETE;\n CLEAR(pobj);\n }\n}\nexport const objectClear = (obj, name) => {\n if (!isUndefined(parsePath(obj, name))) {\n parsePath(obj, name, TODO_DELETE);\n\n CLEAR(obj);\n }\n};\n","import React, { Component, Children, cloneElement, createElement } from 'react';\nimport PropTypes from 'prop-types';\nimport FormContext from './context';\nimport * as utils from './utils';\nimport warning from 'warning';\n\nexport const FORM_VALIDATE_RESULT = 'FORM_VALIDATE_RESULT';\n\nlet requestFrame, cancelFrame;\n\nif (typeof requestAnimationFrame === 'function') {\n requestFrame = requestAnimationFrame;\n cancelFrame = cancelAnimationFrame;\n} else {\n requestFrame = setTimeout;\n cancelFrame = clearTimeout;\n}\n\nclass Form extends Component {\n static displayName = 'React.Formutil.Form';\n\n static propTypes = {\n render: PropTypes.func,\n component: utils.checkComponentPropType,\n children(props, ...args) {\n let pt = PropTypes.oneOfType([PropTypes.func, PropTypes.node]);\n\n if (!props.render && !props.component) {\n pt = pt.isRequired;\n }\n\n return pt(props, ...args);\n },\n $defaultValues: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),\n $defaultStates: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),\n $onFormChange: PropTypes.func,\n $validator: PropTypes.func,\n $processer: PropTypes.func,\n $ref: PropTypes.oneOfType([\n PropTypes.func,\n PropTypes.shape({\n current: PropTypes.any\n })\n ])\n };\n\n static defaultProps = {\n $defaultValues: {},\n $defaultStates: {}\n };\n\n $$formPending;\n $$formValidatePromise;\n\n $$registers = {};\n $$deepRegisters = {};\n\n constructor(props) {\n super(props);\n\n this.$$defaultInitialize();\n }\n\n getFormContext() {\n return {\n $$registers: this.$$registers,\n $$register: this.$$register,\n $$unregister: this.$$unregister,\n $$onChange: this.$$onChange,\n $$getDefault: this.$$getDefault,\n $formutil: this.$formutil\n };\n }\n\n $$regDuplications = {};\n $$duplicateTimer;\n $$checkDuplication = () => {\n const { $$regDuplications } = this;\n let hasDup;\n\n utils.objectEach($$regDuplications, ([$curRegistered, $handler], name) => {\n warning($curRegistered.$$reserved, `The Field with a name '${name}' has been registered!`);\n\n $handler.$$reset($curRegistered.$getState());\n\n hasDup = delete $$regDuplications[name];\n });\n\n if (hasDup) {\n this.$render();\n }\n };\n\n /*\n * @desc 注册或者替换(preName)Field\n */\n $$register = (name, $handler, prevName) => {\n this.$$unregister(prevName, $handler);\n\n if (name) {\n const $curRegistered = this.$$getRegister(name);\n\n if ($curRegistered) {\n cancelFrame(this.$$duplicateTimer);\n\n this.$$regDuplications[name] = [$curRegistered, $handler];\n this.$$duplicateTimer = requestFrame(this.$$checkDuplication);\n } else {\n this.$$fieldChangedQueue.push({\n name,\n $newValue: $handler.$getState().$value\n });\n\n utils.objectClear(this.$$defaultValues, name);\n }\n\n this.$$registers[($handler.$name = name)] = $handler;\n\n this.createDeepRegisters();\n this.$render();\n }\n };\n\n $$unregister = (name, $handler, $$reserved) => {\n if (name) {\n if (name in this.$$regDuplications) {\n const [$curRegistered, $handler] = this.$$regDuplications[name];\n\n this.$$fieldChangedQueue.push({\n name,\n $newValue: $handler.$getState().$value,\n $prevValue: $curRegistered.$getState().$value\n });\n\n delete this.$$regDuplications[name];\n } else if (this.$$registers[name] === $handler) {\n if ($$reserved) {\n $handler.$$reserved = true;\n } else {\n delete this.$$registers[name];\n\n this.$$fieldChangedQueue.push({\n name,\n $prevValue: $handler.$getState().$value\n });\n\n utils.objectClear(this.$$defaultValues, name);\n }\n }\n\n this.createDeepRegisters();\n this.$render();\n }\n };\n\n $$defaultInitialize = () => {\n const { $defaultValues, $defaultStates } = this.props;\n\n this.$$defaultValues = this.$$deepParseObject(\n utils.deepClone(utils.isFunction($defaultValues) ? $defaultValues(this.props) || {} : $defaultValues)\n );\n this.$$defaultStates = this.$$deepParseObject(\n utils.deepClone(utils.isFunction($defaultStates) ? $defaultStates(this.props) || {} : $defaultStates)\n );\n };\n\n $$getDefault = () => ({\n $$defaultStates: this.$$defaultStates,\n $$defaultValues: this.$$defaultValues\n });\n\n $$deepParseObject(mayWeakObj, deepObj = {}) {\n utils.objectEach(mayWeakObj, (data, name) => utils.parsePath(deepObj, name, data));\n\n return deepObj;\n }\n\n $$triggerChangeTimer;\n $$fieldChangedQueue = [];\n $$triggerFormChange = () => {\n if (this.$$fieldChangedQueue.length) {\n const $$fieldChangedQueue = [...this.$$fieldChangedQueue];\n\n this.$$fieldChangedQueue.length = 0;\n\n const $newValues = {};\n const $prevValues = {};\n const $$registers = this.$$registers;\n let hasFormChanged = false;\n\n $$fieldChangedQueue.forEach(item => {\n if (!(item.name in $$registers)) {\n delete item.$newValue;\n }\n\n if (item.$newValue !== item.$prevValue) {\n if ('$newValue' in item && '$prevValue' in item) {\n const $handler = this.$$getRegister(item.name);\n\n if ($handler) {\n $handler.$$triggerChange(item);\n }\n }\n\n '$newValue' in item && utils.parsePath($newValues, item.name, item.$newValue);\n '$prevValue' in item && utils.parsePath($prevValues, item.name, item.$prevValue);\n\n hasFormChanged = true;\n }\n });\n\n if (hasFormChanged) {\n if (utils.isFunction(this.props.$validator)) {\n this.$$formValidate();\n }\n\n if (utils.isFunction(this.props.$onFormChange)) {\n this.props.$onFormChange(this.$formutil, $newValues, $prevValues);\n }\n }\n }\n };\n\n createDeepRegisters = () => (this.$$deepRegisters = this.$$deepParseObject(this.$$registers));\n\n $$getRegister = name => {\n if (name) {\n const field = this.$$registers[name] || utils.parsePath(this.$$deepRegisters, name);\n\n if (field) {\n return field;\n }\n }\n };\n\n $$formValidate = callback =>\n (this.$$formValidatePromise = new Promise(resolve => {\n const { $validator } = this.props;\n\n let $breakAsyncHandler;\n let $shouldCancelPrevAsyncValidate;\n let prevCallback;\n let validation;\n\n const result = $validator(this.$formutil.$params, this.formtutil);\n const execCallback = $formutil =>\n resolve(utils.runCallback(callback, utils.runCallback(prevCallback, $formutil)));\n\n if (utils.isPromise(result)) {\n if (!this.$$formPending) {\n this.$$formPending = true;\n\n this.$render();\n }\n\n $shouldCancelPrevAsyncValidate = setCallback => ($breakAsyncHandler = setCallback(execCallback));\n\n validation = result\n .then(() => void 0, reason => reason)\n .then(reason => {\n if ($breakAsyncHandler) {\n return $breakAsyncHandler;\n }\n\n this.$shouldCancelPrevAsyncValidate = null;\n\n this.$$formPending = false;\n\n return this.$$setFormErrors(reason, execCallback);\n });\n } else {\n if (this.$$formPending) {\n this.$$formPending = false;\n }\n\n validation = this.$$setFormErrors(result, execCallback);\n }\n\n if (this.$shouldCancelPrevAsyncValidate) {\n this.$shouldCancelPrevAsyncValidate(callback => {\n prevCallback = callback;\n\n return validation;\n });\n }\n\n this.$shouldCancelPrevAsyncValidate = $shouldCancelPrevAsyncValidate;\n }));\n\n $$setFormErrors = (validResults, callback) => {\n if (validResults && (validResults instanceof Error || typeof validResults !== 'object')) {\n warning(\n false,\n `The result of $validator in
should always return None(null,undefined) or an object contains error message of Field.`\n );\n\n return this.$render(callback);\n }\n\n return this.$$setStates(\n validResults || {},\n (result, handler) => {\n const { $error = {} } = handler.$getState();\n\n if (result) {\n return {\n $error: {\n ...$error,\n [FORM_VALIDATE_RESULT]: result\n }\n };\n }\n\n if ($error[FORM_VALIDATE_RESULT]) {\n delete $error[FORM_VALIDATE_RESULT];\n\n return {\n $error\n };\n }\n\n return;\n },\n callback,\n true\n );\n };\n\n $getField = name => {\n const field = this.$$getRegister(name);\n\n warning(!name || field, `$getField('${name}') fail to find the matched Field. Maybe it has been unmounted.`);\n warning(name, `You should pass a name of the mounted Field to $getField().`);\n\n if (field) {\n return field.$new();\n }\n };\n\n $$onChange = (name, $state, callback) =>\n this.$setStates(\n {\n [name]: $state\n },\n callback\n );\n\n $$setStates = ($stateTree = {}, processer, callback, force) => {\n const $parsedTree = this.$$deepParseObject($stateTree);\n\n utils.objectEach(this.$$registers, (handler, name) => {\n const data = name in $stateTree ? $stateTree[name] : utils.parsePath($parsedTree, name);\n\n if (!utils.isUndefined(data) || force) {\n const $newState = processer(data, handler);\n\n if ($newState) {\n const $prevValue = this.$formutil.$weakParams[name];\n const { $value: $newValue } = handler.$$merge($newState);\n\n handler.$$detectChange($newState);\n\n if ('$value' in $newState || '$viewValue' in $newState) {\n const findItem = utils.arrayFind(this.$$fieldChangedQueue, item => item.name === name);\n\n if (findItem) {\n if (!('$prevValue' in findItem)) {\n findItem.$prevValue = findItem.$newValue;\n }\n\n findItem.$newValue = $newValue;\n } else {\n this.$$fieldChangedQueue.push({\n name,\n $newValue,\n $prevValue\n });\n }\n }\n }\n }\n });\n\n return this.$render(callback);\n };\n\n componentDidMount() {\n utils.createRef(this.props.$ref, this.$formutil);\n }\n\n componentDidUpdate(prevProps) {\n utils.createRef(this.props.$ref, this.$formutil);\n\n cancelFrame(this.$$triggerChangeTimer);\n\n // ensure this calls to access the newest $formutil\n this.$$triggerChangeTimer = requestFrame(() => {\n this.$$triggerFormChange();\n });\n }\n\n componentWillUnmount() {\n utils.createRef(this.props.$ref, null);\n }\n\n $render = callback =>\n new Promise(resolve => this.forceUpdate(() => resolve(utils.runCallback(callback, this.$formutil))));\n\n $validates = (...args) => {\n let callback;\n\n if (utils.isFunction(args[args.length - 1])) {\n callback = args.pop();\n }\n\n if (args.length) {\n const flatter = names => {\n names.forEach(name => {\n if (Array.isArray(name)) {\n flatter(name);\n } else {\n const handler = this.$getField(name);\n\n if (handler) {\n handler.$validate();\n }\n }\n });\n };\n\n flatter(args);\n } else {\n utils.objectEach(this.$$registers, handler => handler.$validate());\n\n if (utils.isFunction(this.props.$validator)) {\n this.$$formValidate();\n }\n }\n\n return this.$onValidates(callback);\n };\n\n $onValidates = callback => {\n const filedValidatePromises = Object.keys(this.$$registers).map(name => this.$$registers[name].$onValidate());\n\n filedValidatePromises.push(this.$$formValidatePromise);\n\n return Promise.all(filedValidatePromises).then(() => utils.runCallback(callback, this.$formutil));\n };\n\n $validate = (name, callback) => {\n const handler = this.$getField(name);\n\n if (handler) {\n return handler.$validate(callback);\n }\n\n return utils.runCallback(callback);\n };\n\n $reset = ($stateTree, callback) => {\n this.$$defaultInitialize();\n\n if (utils.isFunction($stateTree)) {\n callback = $stateTree;\n $stateTree = {};\n }\n\n return this.$$setStates($stateTree, ($state, handler) => handler.$$reset($state), callback, true);\n };\n\n $setStates = ($stateTree, callback) => this.$$setStates($stateTree, $state => $state, callback);\n\n $setValues = ($valueTree, callback) => {\n this.$$deepParseObject(utils.deepClone($valueTree), this.$$defaultValues);\n\n utils.CLEAR(this.$$defaultValues);\n\n return this.$$setStates($valueTree, $value => ({ $value }), callback);\n };\n\n $setFocuses = ($focusedTree, callback) => this.$$setStates($focusedTree, $focused => ({ $focused }), callback);\n $setDirts = ($dirtyTree, callback) => this.$$setStates($dirtyTree, $dirty => ({ $dirty }), callback);\n $setTouches = ($touchedTree, callback) => this.$$setStates($touchedTree, $touched => ({ $touched }), callback);\n $setPendings = ($pendingTree, callback) => this.$$setStates($pendingTree, $pending => ({ $pending }), callback);\n $setErrors = ($errorTree, callback) => this.$$setStates($errorTree, $error => ({ $error }), callback);\n\n $batchState = ($state, callback) => this.$setStates(utils.objectMap(this.$$registers, () => $state), callback);\n $batchDirty = ($dirty, callback) =>\n this.$batchState(\n {\n $dirty\n },\n callback\n );\n\n $batchTouched = ($touched, callback) =>\n this.$batchState(\n {\n $touched\n },\n callback\n );\n\n $batchFocused = ($focused, callback) =>\n this.$batchState(\n {\n $focused\n },\n callback\n );\n\n $batchPending = ($pending, callback) =>\n this.$batchState(\n {\n $pending\n },\n callback\n );\n\n $batchError = ($error, callback) =>\n this.$batchState(\n {\n $error\n },\n callback\n );\n\n _render() {\n const $formutil = this.$formutil;\n let { children, render, component } = this.props;\n\n if (component) {\n return createElement(component, { $formutil });\n }\n\n if (utils.isFunction(render)) {\n return render($formutil);\n }\n\n if (utils.isFunction(children)) {\n return children($formutil);\n }\n\n return Children.map(children, child =>\n child && utils.isComponent(child.type)\n ? cloneElement(child, {\n $formutil\n })\n : child\n );\n }\n\n render() {\n const { $processer } = this.props;\n const $stateArray = Object.keys(this.$$registers).map(path => ({\n path,\n $state: this.$$registers[path].$getState()\n }));\n\n const $weakParams = utils.toObject($stateArray, ($params, { path, $state }) => {\n if ($processer) {\n $processer($state, path);\n }\n\n if ('$value' in $state && ($state.$dirty || !utils.isUndefined($state.$value))) {\n $params[path] = $state.$value;\n }\n });\n\n const $pureParams = utils.toObject(\n $stateArray,\n ($params, { path, $state }) => path in $weakParams && utils.parsePath($params, path, $weakParams[path])\n );\n\n const $invalid = $stateArray.some(({ $state }) => $state.$invalid);\n const $dirty = $stateArray.some(({ $state }) => $state.$dirty);\n const $touched = $stateArray.some(({ $state }) => $state.$touched);\n const $focused = $stateArray.some(({ $state }) => $state.$focused);\n const $pending = this.$$formPending || $stateArray.some(({ $state }) => $state.$pending);\n\n const $formutil = (this.$formutil = {\n $$registers: { ...this.$$registers },\n $$deepRegisters: this.$$deepRegisters,\n $states: utils.toObject($stateArray, ($states, { path, $state }) => utils.parsePath($states, path, $state)),\n $params: {\n ...this.$$defaultValues,\n ...$pureParams\n },\n $errors: utils.toObject($stateArray, ($errors, { path, $state }) => {\n if ($state.$invalid) {\n utils.parsePath($errors, path, $state.$error);\n }\n }),\n $dirts: utils.toObject($stateArray, ($dirts, { path, $state }) =>\n utils.parsePath($dirts, path, $state.$dirty)\n ),\n $touches: utils.toObject($stateArray, ($touches, { path, $state }) =>\n utils.parsePath($touches, path, $state.$touched)\n ),\n $focuses: utils.toObject($stateArray, ($focuses, { path, $state }) =>\n utils.parsePath($focuses, path, $state.$focused)\n ),\n $pendings: utils.toObject($stateArray, ($pendings, { path, $state }) =>\n utils.parsePath($pendings, path, $state.$pending)\n ),\n\n $weakStates: utils.toObject($stateArray, ($states, { path, $state }) => ($states[path] = $state)),\n $weakParams,\n $weakErrors: utils.toObject($stateArray, ($errors, { path, $state }) => {\n if ($state.$invalid) {\n $errors[path] = $state.$error;\n }\n }),\n $weakDirts: utils.toObject($stateArray, ($dirts, { path, $state }) => ($dirts[path] = $state.$dirty)),\n $weakTouches: utils.toObject(\n $stateArray,\n ($touches, { path, $state }) => ($touches[path] = $state.$touched)\n ),\n $weakFocuses: utils.toObject(\n $stateArray,\n ($focuses, { path, $state }) => ($focuses[path] = $state.$focused)\n ),\n $weakPendings: utils.toObject(\n $stateArray,\n ($weakPendings, { path, $state }) => ($weakPendings[path] = $state.$pending)\n ),\n\n $getFirstError(name) {\n if (name) {\n const $fieldutil = $formutil.$getField(name);\n\n return $fieldutil && $fieldutil.$getFirstError();\n }\n\n for (let name in $formutil.$weakErrors) {\n const $fieldError = $formutil.$weakErrors[name];\n\n for (let key in $fieldError) {\n return $fieldError[key] instanceof Error ? $fieldError[key].message : $fieldError[key];\n }\n }\n },\n\n $render: this.$render,\n\n $getField: this.$getField,\n $onValidates: this.$onValidates,\n\n // get the newest $formutil\n $new: () => this.$formutil,\n\n $setStates: this.$setStates,\n $setValues: this.$setValues,\n $setErrors: this.$setErrors,\n $setTouches: this.$setTouches,\n $setDirts: this.$setDirts,\n $setFocuses: this.$setFocuses,\n\n $batchState: this.$batchState,\n $batchTouched: this.$batchTouched,\n $batchDirty: this.$batchDirty,\n $batchFocused: this.$batchFocused,\n\n $reset: this.$reset,\n $validates: this.$validates,\n $validate: this.$validate,\n\n $valid: !$invalid,\n $invalid,\n $dirty,\n $pristine: !$dirty,\n $touched,\n $untouched: !$touched,\n $focused,\n $pending\n });\n\n return