Formily 在 react 层的实现,内置表单状态核心管理(@formily/react), 通过结合 React 和核心管理机制,提供给开发者 API 可以快速操作表单,以及提供相应 UI 层渲染的支持。 @formily/react 中主要包含了以下部分:
- Form 表单容器
- Field 表单字段
- VirtualField 虚拟表单字段
- FormaSpy 表单替身
- FormProvider 表单核心提供者
- FormConsumer 表单核心消费者(即将废弃,请使用 FormSpy)
- createFormActions 创建表单核心操作 API 实例
- createAsyncFormActions 创建表单核心操作 API 实例(异步)
- FormEffectHooks 表单生命周期 hook
npm install --save @formily/react
- 使用方式
- Components
- Hook
- API
- Interfaces
IForm
Imutators
IFormActions
IFormAsyncActions
IFieldState
IVirtualFieldState
IFormSpyProps
IFieldHook
IVirtualFieldHook
ISpyHook
SyncValidateResponse
AsyncValidateResponse
ValidateResponse
InternalFormats
CustomValidator
ValidateDescription
ValidateArrayRules
ValidatePatternRules
IFieldAPI
IVirtualFieldAPI
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
FormPath,
createFormActions,
FormSpy,
FormProvider,
FormConsumer,
FormEffectHooks
} from '@formily/react'
const { onFormInit$, onFormInputChange$, onFieldInputChange$ } = FormEffectHooks
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={() => {
onFormInit$().subscribe(() => {
console.log('初始化')
})
onFieldInputChange$().subscribe(state => {
console.log('输入变化', state)
})
}}
onChange={() => {}}
>
<React.Fragment>
<label>username: </label>
<Field name="username">
{({ state, mutators }) => (
<React.Fragment>
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
{state.errors}
{state.warnings}
</React.Fragment>
)}
</Field>
</React.Fragment>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:以输入框为例,如何快速绑定表单字段,后续例子都基于此字段拓展。
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<div>
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
{state.errors}
{state.warnings}
</div>
)}
</Field>
)
示例:必填校验 + error 类型校验 + warning 类型校验 + 自定义校验 校验的类型可以是 ValidatePatternRules,即 InternalFormats | CustomValidator | ValidateDescription | ValidateArrayRules
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<h5>required validation</h5>
<span>username</span>
<InputField name="username" required />
<h5>error type validation</h5>
<span>age</span>
<InputField
name="age"
rules={[
val =>
val === undefined
? { type: 'error', message: 'age is required' }
: undefined
]}
/>
<h5>warning type validation</h5>
<span>gender</span>
<InputField
name="gender"
rules={[
val =>
val === undefined
? { type: 'warning', message: 'gender is required' }
: undefined
]}
/>
<h5>built-in validation default to error type validation</h5>
<span>id</span>
<InputField
name="id"
rules={[
{
format: 'number',
message: 'id is not a number.'
}
]}
/>
<h5>custom validation</h5>
<span>verifyCode</span>
<InputField
name="verifyCode"
rules={[
{
validator(value) {
return value === undefined
? 'This field can not be empty, please enter {{scope.outerVariable}}'
: undefined
},
scope: {
outerVariable: '456'
}
},
{
validator(value) {
return value === '456'
? { type: 'error', message: 'This field can not be 456' }
: undefined
}
}
]}
/>
<div>
<button
onClick={() => {
const result = actions.validate()
console.log(actions.getFormState(state => state.values))
result.then(validateResp => {
console.log(validateResp)
})
}}
>
validate
</button>
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:用户信息 user(username, age)
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<span>user</span>
<Field
name="user"
initialValue={{
username: undefined,
age: undefined
}}
>
{({ state, mutators }) => {
return (
<React.Fragment>
{Object.keys(state.value).map(key => {
if (!mutators.exist(key)) return
return (
<div key={key}>
<span>{key}</span>
<InputField name={`user.${key}`} />
<button
onClick={() => {
mutators.remove(key)
}}
>
x
</button>
</div>
)
})}
<button
onClick={() => {
mutators.change({
...state.value,
[new Date().getTime()]: new Date().getTime()
})
}}
>
+
</button>
<button
onClick={() => {
console.log(
'values',
actions.getFormState(state => state.values)
)
}}
>
print
</button>
</React.Fragment>
)
}}
</Field>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:用户 id 列表,增删改查
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<Field name="idList" initialValue={['1', '2', '3']}>
{({ state, mutators }) => {
return (
<React.Fragment>
{state.value.map((item, index) => {
return (
<div key={index}>
<InputField name={`idList[${index}]`} />
<button onClick={() => mutators.remove(index)}>
Remove
</button>
</div>
)
})}
<button onClick={() => mutators.push()}>Add Item</button>
</React.Fragment>
)
}}
</Field>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:用户 id 列表,增删改查
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<Field
name="userList"
initialValue={[
{ username: 'bobby', age: 21 },
{ username: 'lily', age: 20 }
]}
>
{({ state, mutators }) => {
return (
<React.Fragment>
{state.value.map((item, index) => {
return (
<div key={index}>
<Field name={`userList[${index}]`} initialValue={{}}>
{({ state: innerState, mutators: innerMutator }) => {
return (
<React.Fragment>
{Object.keys(innerState.value).map(key => {
if (!innerMutator.exist(key)) return
return (
<React.Fragment key={key}>
<InputField
name={`userList[${index}].${key}`}
/>
<button
onClick={() => {
innerMutator.remove(key)
}}
>
x
</button>
</React.Fragment>
)
})}
<button
onClick={() => {
innerMutator.change({
...innerState.value,
[new Date().getTime()]: new Date().getTime()
})
}}
>
+
</button>
</React.Fragment>
)
}}
</Field>
<button onClick={() => mutators.remove(index)}>
Remove
</button>
</div>
)
})}
<button
onClick={() =>
mutators.push({
username: undefined,
age: undefined
})
}
>
Add Item
</button>
<button
onClick={() =>
console.log(actions.getFormState(state => state.values))
}
>
print
</button>
</React.Fragment>
)
}}
</Field>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例: display 与 visible 对 values 的影响
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
LifeCycleTypes,
FormSpy
} from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CheckedField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
type="checkbox"
onChange={() => {
mutators.change(!state.value)
}}
checked={!!state.value}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={($, { validate, setFieldState }) => {
$(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
setFieldState('displayTrigger', state => (state.value = true))
setFieldState('visibleTrigger', state => (state.value = true))
setFieldState('a', state => (state.value = 1))
setFieldState('b', state => (state.value = 2))
})
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'visibleTrigger').subscribe(
fieldState => {
setFieldState('a', state => {
state.visible = fieldState.value
})
}
)
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'displayTrigger').subscribe(
fieldState => {
setFieldState('b', state => {
state.display = fieldState.value
})
}
)
}}
>
<div>
<CheckedField label="visible" name="visibleTrigger" />
<InputField name="a" label="a" />
</div>
<div>
<CheckedField label="display" name="displayTrigger" />
<InputField name="b" label="b" />
</div>
<FormSpy>
{({ state, form }) => {
return (
<code>
<pre>
{JSON.stringify(
form.getFormState(state => state.values),
null,
2
)}
</pre>
</code>
)
}}
</FormSpy>
<button
onClick={() => console.log(actions.getFormState(state => state.values))}
>
print
</button>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:显示及隐藏,修改 props 和 value
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CheckedField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
type="checkbox"
onChange={() => {
mutators.change(!state.value)
}}
checked={!!state.value}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={($, { setFieldState }) => {
$(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
setFieldState('a~', state => (state.visible = false))
})
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
triggerState => {
setFieldState('a~', state => {
state.visible = triggerState.value
})
}
)
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
setFieldState('a-copy', state => {
state.value = fieldState.value
})
})
}}
>
<CheckedField name="trigger" label="show/hide" />
<div>
<InputField label="a" name="a" />
</div>
<div>
<InputField label="a-copy" name="a-copy" />
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:异步切换 select 的 dataSource
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CheckedField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
type="checkbox"
onChange={() => {
mutators.change(!state.value)
}}
checked={!!state.value}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const SelectField = props => (
<Field {...props}>
{({ state, mutators }) => {
const { loading, dataSource = [] } = state.props
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<select
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
>
{dataSource.map(item => (
<option value={item.value}>{item.label}</option>
))}
</select>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={($, { setFieldState }) => {
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
fieldState => {
const dataSource = [
{ label: 'aa', value: 'aa' },
{ label: 'bb', value: 'bb' }
]
setFieldState('sync-source', state => {
state.props.dataSource = fieldState.value ? dataSource : []
})
setFieldState('async-source', state => {
state.props.loading = true
})
setTimeout(() => {
setFieldState('async-source', state => {
state.props.loading = false
state.props.dataSource = fieldState.value ? dataSource : []
})
}, 300)
}
)
}}
>
<CheckedField name="trigger" label="show/reset dataSource" />
<div>
<SelectField label="sync-source" name="sync-source" />
</div>
<div>
<SelectField label="async-source" name="async-source" />
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:初始化校验,字段 change 时自动重新触发校验
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={($, { validate, setFieldState }) => {
$(LifeCycleTypes.ON_FORM_MOUNT).subscribe(() => {
validate()
})
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
setFieldState('a-copy', state => {
state.value = fieldState.value
})
})
}}
>
<InputField label="a" name="a" />
<div>
<InputField label="a-copy" name="a-copy" required />
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:ArrayField 复杂联动
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={($, { validate, setFieldState }) => {
$(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
setFieldState('userList.*.username', state => {
state.visible = false
})
})
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
fieldState => {
setFieldState('userList.*.username', state => {
state.visible = fieldState.value
})
}
)
}}
>
<label>show/hide username</label>
<Field name="trigger">
{({ state, mutators }) => {
return (
<input
type="checkbox"
onChange={mutators.change}
checked={state.value ? 'checked' : undefined}
/>
)
}}
</Field>
<div>
<Field
initialValue={[
{ username: 'bobby', age: 22 },
{ username: 'lily', age: 21 }
]}
name="userList"
>
{({ state, mutators }) => {
return (
<React.Fragment>
{state.value.map((item, index) => {
return (
<div key={index}>
<Field name={`userList[${index}]`} initialValue={{}}>
{({ state: innerState, mutators: innerMutator }) => {
return (
<React.Fragment>
{Object.keys(innerState.value).map(key => {
if (!innerMutator.exist(key)) return
return (
<React.Fragment key={key}>
<InputField
label={key}
name={`userList[${index}].${key}`}
/>
</React.Fragment>
)
})}
<button
onClick={() => {
innerMutator.change({
...innerState.value,
[new Date().getTime()]: new Date().getTime()
})
}}
>
+
</button>
</React.Fragment>
)
}}
</Field>
<button onClick={() => mutators.remove(index)}>
Remove
</button>
</div>
)
})}
<button
onClick={() =>
mutators.push({
username: undefined,
age: undefined
})
}
>
Add Item
</button>
<button
onClick={() =>
console.log(actions.getFormState(state => state.values))
}
>
print
</button>
</React.Fragment>
)
}}
</Field>
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
自定义可复用的 effects
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormEffectHooks } from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CheckedField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
type="checkbox"
onChange={() => {
mutators.change(!state.value)
}}
checked={!!state.value}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const { onFormMount$, onFieldValueChange$ } = FormEffectHooks
const getEffects = () => {
const actions = createFormActions()
onFormMount$().subscribe(() => {
actions.setFieldState('a~', state => (state.visible = false))
})
onFieldValueChange$('trigger').subscribe(triggerState => {
actions.setFieldState('a~', state => {
state.visible = triggerState.value
})
})
onFieldValueChange$('a').subscribe(fieldState => {
actions.setFieldState('a-copy', state => {
state.value = fieldState.value
})
})
}
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={() => {
getEffects()
}}
>
<CheckedField name="trigger" label="show/hide" />
<div>
<InputField label="a" name="a" />
</div>
<div>
<InputField label="a-copy" name="a-copy" />
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
示例:combo username 和 age 字段, 更多用法,请点击FormSpy查看
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
<FormSpy>
{({ state, form }) => {
return (
<div>
name: {form.getFieldValue('username')}
<br />
age: {form.getFieldValue('age')}
</div>
)
}}
</FormSpy>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
文件目录
--app
|---components
|---customForm
示例:跨文件消费表单数据, 更多用法,请参考FormProvider 和 FormSpy
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
FormSpy,
FormProvider
} from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CustomForm = () => {
return (
<Form actions={actions}>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
</Form>
)
}
const App = () => {
return (
<FormProvider>
<CustomForm />
<FormSpy>
{({ state, form }) => {
return (
<div>
name: {form.getFieldValue('username')}
<br />
age: {form.getFieldValue('age')}
</div>
)
}}
</FormSpy>
</FormProvider>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
FormSpy,
FormPath
} from '@formily/react'
const actions = createFormActions()
const App = () => {
return (
<Form actions={actions}>
<label>range input</label>
<Field name="[start,end]">
{({ state, mutators }) => {
const [start, end] = state.value
return (
<div>
<label>start</label>
<input
value={start}
onChange={e => {
mutators.change([e.target.value, end])
}}
/>
<label>end</label>
<input
value={end}
onChange={e => {
mutators.change([start, e.target.value])
}}
/>
</div>
)
}}
</Field>
<button
onClick={() => {
actions.setFormState(state => {
state.values = { start: 'x', end: 'y' }
})
}}
>
set value
</button>
<FormSpy>
{({ state, form }) => {
return (
<div>
Form values:
<code>
<pre>
{JSON.stringify(
form.getFormState(state => state.values),
null,
2
)}
</pre>
</code>
</div>
)
}}
</FormSpy>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
FormSpy,
FormPath
} from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<Field name="{aa:{bb:{cc:destructor1,dd:[destructor2,destructor3],ee}}}">
{({ state, mutators }) => {
return (
<div>
<button
onClick={() => {
mutators.change({
aa: {
bb: {
cc: 123,
dd: [333, 444],
ee: 'abcde'
}
}
})
}}
>
set value
</button>
<div>Field value:</div>
<code>
<pre>{JSON.stringify(state.value, null, 2)}</pre>
</code>
</div>
)
}}
</Field>
<button
onClick={() => {
actions.setFieldState(
FormPath.match(
'[[{aa:{bb:{cc:destructor1,dd:\\[destructor2,destructor3\\],ee}}}]]'
),
state => {
state.value = {
aa: {
bb: {
cc: 'a',
dd: ['b', 'c'],
ee: 'd'
}
}
}
}
)
}}
>
outside set
</button>
<FormSpy>
{({ state, form }) => {
return (
<div>
Form values:
<code>
<pre>
{JSON.stringify(
form.getFormState(state => state.values),
null,
2
)}
</pre>
</code>
</div>
)
}}
</FormSpy>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Form 组件属性定义
interface IFormProps {
//值
value?: any
defaultValue?: any
//初始值
initialValues?: any
// formAPI
actions?: IFormActions | IFormAsyncActions
// 副作用
effects?: IFormEffect<any, IFormActions | IFormAsyncActions>
// IForm实例
form?: IForm
onChange?: (values: Value) => void
onSubmit?: (values: Value) => void | Promise<Value>
onReset?: () => void
onValidateFailed?: (valideted: IFormValidateResult) => void
children?: React.ReactElement | ((form: IForm) => React.ReactElement)
//是否使用脏检查,默认会走immer精确更新
useDirty?: boolean
// 是否可编辑,默认可编辑
editable?: boolean
//是否走悲观校验,遇到第一个校验失败就停止后续校验
validateFirst?: boolean
}
Field 组件属性定义
interface IFieldStateUIProps {
//节点路径
path?: FormPathPattern
//节点路径
nodePath?: FormPathPattern
//数据路径
dataPath?: FormPathPattern
//数据路径
name?: string
//字段值,与values[0]是恒定相等
value?: any
//字段多参值,比如字段onChange触发时,给事件回调传了多参数据,那么这里会存储所有参数的值
values?: any[]
//初始值
initialValue?: any
//数据与样式是否可见
visible?: boolean
//样式是否可见
display?: boolean
//字段扩展属性
props?: FieldProps
//校验规则,具体类型描述参考后面文档
rules?: ValidatePatternRules[]
//是否必填
required?: boolean
//字段是否可编辑
editable?: boolean
//字段是否走脏检查
useDirty?: boolean
//字段状态计算容器,主要用于扩展核心联动规则
computeState?: (draft: IFieldState, prevState: IFieldState) => void
// 触发校验类型
triggerType?: 'onChange' | 'onBlur'
// 值格式化函数,从浏览器event中获取value
getValueFromEvent?: (...args: any[]) => any
children?: React.ReactElement | ((api: IFieldAPI) => React.ReactElement)
}
用法
例子:各种类型的字段
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<div>
<h5>Basic Field</h5>
<Field name="id">
{({ state, mutator }) => {
return <input value={state.value} onChange={mutator} />
}}
</Field>
</div>
<div>
<h5>Object Field</h5>
<Field
name="user"
initialValue={{
username: undefined,
age: undefined
}}
>
{({ state, mutators }) => {
return (
<React.Fragment>
{Object.keys(state.value).map(key => {
if (!mutators.exist(key)) return
return (
<div key={key}>
<span>{key}</span>
<InputField name={`user.${key}`} />
<button
onClick={() => {
mutators.remove(key)
}}
>
x
</button>
</div>
)
})}
<button
onClick={() => {
mutators.change({
...state.value,
[new Date().getTime()]: new Date().getTime()
})
}}
>
+
</button>
</React.Fragment>
)
}}
</Field>
</div>
<div>
<h5>ArrayField Field</h5>
<Field name="idList" initialValue={['1', '2', '3']}>
{({ state, mutators }) => {
return (
<React.Fragment>
{state.value.map((item, index) => {
return (
<div key={index}>
<InputField name={`idList[${index}]`} />
<button onClick={() => mutators.remove(index)}>
Remove
</button>
</div>
)
})}
<button onClick={() => mutators.push()}>Add Item</button>
</React.Fragment>
)
}}
</Field>
</div>
<div>
<h5>ArrayObject Field</h5>
<Field
name="userList"
initialValue={[
{ username: 'bobby', age: 21 },
{ username: 'lily', age: 20 }
]}
>
{({ state, mutators }) => {
return (
<React.Fragment>
{state.value.map((item, index) => {
return (
<div key={index}>
<Field name={`userList[${index}]`} initialValue={{}}>
{({ state: innerState, mutators: innerMutator }) => {
return (
<React.Fragment>
{Object.keys(innerState.value).map(key => {
if (!innerMutator.exist(key)) return
return (
<React.Fragment key={key}>
<InputField
name={`userList[${index}].${key}`}
/>
<button
onClick={() => {
innerMutator.remove(key)
}}
>
x
</button>
</React.Fragment>
)
})}
<button
onClick={() => {
innerMutator.change({
...innerState.value,
[new Date().getTime()]: new Date().getTime()
})
}}
>
+
</button>
</React.Fragment>
)
}}
</Field>
<button onClick={() => mutators.remove(index)}>
Remove
</button>
</div>
)
})}
<button
onClick={() =>
mutators.push({
username: undefined,
age: undefined
})
}
>
Add Item
</button>
</React.Fragment>
)
}}
</Field>
</div>
<button
onClick={() => console.log(actions.getFormState(state => state.values))}
>
print
</button>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
VirtualField 组件属性定义
interface IVirtualFieldProps {
//节点路径
path?: FormPathPattern
//节点路径
nodePath?: FormPathPattern
//数据路径
dataPath?: FormPathPattern
//数据与样式是否可见
visible?: boolean
//样式是否可见
display?: boolean
//数据路径
name?: string
//字段扩展属性
props?: FieldProps
//字段是否走脏检查
useDirty?: boolean
//字段状态计算容器,主要用于扩展核心联动规则
computeState?: (draft: IFieldState, prevState: IFieldState) => void
children?: React.ReactElement | ((api: IFieldAPI) => React.ReactElement)
}
用法
例子:动态设置布局组件的属性
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, VirtualField } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const Layout = ({ children, width = '100px', height = '100px' }) => {
return (
<div style={{ border: '1px solid #999', width, height }}>{children}</div>
)
}
const App = () => {
return (
<Form actions={actions}>
<Field name="user" initialValue={{}}>
{({ state, mutator }) => {
return (
<VirtualField name="user.layout">
{({ state: layoutState }) => {
return (
<Layout
width={layoutState.props.width}
height={layoutState.props.height}
>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
</Layout>
)
}}
</VirtualField>
)
}}
</Field>
<button
onClick={() => {
// some where dynamic change layout's props
actions.setFieldState('user.layout', state => {
state.props.width = '200px'
state.props.height = '200px'
})
}}
>
change layout
</button>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
FormSpy 组件属性定义
interface IFormSpyProps {
// 选择器, 如:[ LifeCycleTypes.ON_FORM_SUBMIT_START, LifeCycleTypes.ON_FORM_SUBMIT_END ]
selector?: string[] | string
// reducer函数,状态叠加处理,action为当前命中的生命周期的数据
reducer?: (
state: any,
action: { type: string; payload: any },
form: IForm
) => any
children?: React.ReactElement | ((api: IFormSpyAPI) => React.ReactElement)
}
用法
例子 1: 实现一个统计表单 values 改变的计数器
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
FormSpy,
LifeCycleTypes
} from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
<FormSpy
selector={LifeCycleTypes.ON_FORM_VALUES_CHANGE}
reducer={(state, action, form) => ({
count: state.count ? state.count + 1 : 1
})}
>
{({ state, type, form }) => {
return <div>count: {state.count || 0}</div>
}}
</FormSpy>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
例子 2:实现常用 combo 组件
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy } from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const App = () => {
return (
<Form actions={actions}>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
<FormSpy>
{({ state, form }) => {
return (
<div>
name: {form.getFieldValue('username')}
<br />
age: {form.getFieldValue('age')}
</div>
)
}}
</FormSpy>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
与 FormSpy 搭配使用,常用与跨文件通信
用法
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
FormSpy,
FormProvider
} from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CustomForm = () => {
return (
<Form actions={actions}>
<label>username</label>
<InputField name="username" />
<label>age</label>
<InputField name="age" />
</Form>
)
}
const App = () => {
return (
<FormProvider>
<CustomForm />
<FormSpy>
{({ state, form }) => {
return (
<div>
name: {form.getFieldValue('username')}
<br />
age: {form.getFieldValue('age')}
</div>
)
}}
</FormSpy>
</FormProvider>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
FormConsumer 组件属性定义
interface IFormConsumerProps {
// 选择器, 如:[ LifeCycleTypes.ON_FORM_SUBMIT_START, LifeCycleTypes.ON_FORM_SUBMIT_END ]
selector?: string[] | string
children?:
| React.ReactElement
| ((api: IFormConsumerAPI) => React.ReactElement)
}
使用 useFormEffects 可以实现局部 effect 的表单组件,效果同:简单联动 注意:监听的生命周期是从
ON_FORM_MOUNT
开始
签名
(effects: IFormEffect): void
import React from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
createFormActions,
useFormEffects,
LifeCycleTypes
} from '@formily/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const CheckedField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
type="checkbox"
onChange={() => {
mutators.change(!state.value)
}}
checked={!!state.value}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const FormFragment = () => {
useFormEffects(($, { setFieldState }) => {
$(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
setFieldState('a~', state => (state.visible = false))
})
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
triggerState => {
setFieldState('a~', state => {
state.visible = triggerState.value
})
}
)
$(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
setFieldState('a-copy', state => {
state.value = fieldState.value
})
})
})
return (
<React.Fragment>
<CheckedField name="trigger" label="show/hide" />
<div>
<InputField label="a" name="a" />
</div>
<div>
<InputField label="a-copy" name="a-copy" />
</div>
</React.Fragment>
)
}
const App = () => {
return (
<Form actions={actions}>
<FormFragment />
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
使用 useFormState 为自定义组件提供 FormState 扩展和管理能力
签名
(defaultState: T): [state: IFormState, setFormState: (state?: IFormState) => void]
用法
import React, { useRef } from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
VirtualField,
createFormActions,
createEffectHook,
useForm,
useFormState,
useFormEffects,
useFieldState,
LifeCycleTypes
} from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const actions = createFormActions()
const FormFragment = props => {
const [formState, setFormState] = useFormState({ extendVar: 0 })
const { extendVar } = formState
return (
<div>
<button
onClick={() => {
setFormState({ extendVar: extendVar + 1 })
}}
>
add
</button>
<div>count: {extendVar}</div>
</div>
)
}
const App = () => {
return (
<Form actions={actions}>
<FormFragment />
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
使用 useFieldState 为自定义组件提供状态管理能力
签名
(defaultState: T): [state: IFieldState, setFieldState: (state?: IFieldState) => void]
import React, { useRef } from 'react'
import ReactDOM from 'react-dom'
import {
Form,
Field,
VirtualField,
createFormActions,
createEffectHook,
useForm,
useFormEffects,
useFieldState,
LifeCycleTypes
} from '@formily/react'
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => {
const loading = state.props.loading
return (
<React.Fragment>
{props.label && <label>{props.label}</label>}
{loading ? (
' loading... '
) : (
<input
disabled={!state.editable}
value={state.value || ''}
onChange={mutators.change}
onBlur={mutators.blur}
onFocus={mutators.focus}
/>
)}
<span style={{ color: 'red' }}>{state.errors}</span>
<span style={{ color: 'orange' }}>{state.warnings}</span>
</React.Fragment>
)
}}
</Field>
)
const changeTab$ = createEffectHook('changeTab')
const actions = createFormActions()
const TabFragment = props => {
const [fieldState, setLocalFieldState] = useFieldState({ current: 0 })
const { current } = fieldState
const { children, dataSource, form } = props
const update = cur => {
form.notify('changeTab', cur)
setLocalFieldState({
current: cur
})
}
useFormEffects(($, { setFieldState }) => {
dataSource.forEach((item, itemIdx) => {
setFieldState(item.name, state => {
state.display = itemIdx === current
})
})
changeTab$().subscribe(idx => {
dataSource.forEach((item, itemIdx) => {
setFieldState(item.name, state => {
state.display = itemIdx === idx
})
})
})
})
const btns = dataSource.map((item, idx) => {
const focusStyle =
idx === current ? { color: '#fff', background: 'blue' } : {}
return (
<button
style={focusStyle}
onClick={() => {
update(idx)
}}
>
{item.label}
</button>
)
})
return btns
}
const FormTab = props => {
return (
<VirtualField name="layout_tab">
{({ form }) => {
return <TabFragment {...props} form={form} />
}}
</VirtualField>
)
}
const App = () => {
return (
<Form actions={actions}>
<FormTab
dataSource={[
{ label: 'tab-1', name: 'username' },
{ label: 'tab-2', name: 'age' }
]}
/>
<div>
<InputField name="username" label="username" />
<InputField name="age" label="age" />
</div>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
获取一个 IForm 实例
签名
type useForm = <
Value = any,
DefaultValue = any,
EffectPayload = any,
EffectAction = any
>(
props: IFormProps<Value, DefaultValue, EffectPayload, EffectAction>
) => IForm
用法
import { useForm } from '@formily/react'
const FormFragment = () => {
const form = useForm()
return <div>{form.getFieldValue('username')}</div>
}
获取一个 IFieldHook 实例
签名
type useField = (options: IFieldStateUIProps): IFieldHook
用法
import { useField } from '@formily/react'
const FormFragment = props => {
const { form, state, props: fieldProps, mutators } = useField({
name: 'username'
})
return (
<input
{...fieldProps}
{...props}
value={state.value}
onChange={mutators.change}
/>
)
}
获取一个 IVirtualFieldHook 实例
签名
type UseVirtualField = (options: IVirtualFieldStateProps): IVirtualFieldHook
用法
import { UseVirtualField } from '@formily/react'
const FormFragment = props => {
const { form, state, props: fieldProps } = UseVirtualField({
name: 'username'
})
return (
<div style={{ width: fieldProps.width, height: fieldProps.height }}>
{props.children}
</div>
)
}
签名
type useFormSpy = (props: IFormSpyProps): ISpyHook
用法
import { useFormSpy, LifeCycleTypes } from '@formily/react'
const FormFragment = props => {
const { form, state, type } = useFormSpy({
selector: LifeCycleTypes.ON_FORM_VALUES_CHANGE,
reducer: (state, action, form) => ({
count: state.count ? state.count + 1 : 1
})
})
return (
<div>
<div>count: {state.count || 0}</div>
</div>
)
}
整体完全继承@formily/core, 下面只列举@formily/react 的特有 API
创建一个 IFormActions 实例
签名
createFormActions(): IFormActions
用法
import { createFormActions } from '@formily/react'
const actions = createFormActions()
console.log(actions.getFieldValue('username'))
创建一个 IFormAsyncActions 实例,成员方法 同IFormActions, 但是调用 API 返回的结果是异步的(promise)。
签名
createAsyncFormActions(): IFormAsyncActions
用法
import { createAsyncFormActions } from '@formily/react'
const actions = createAsyncFormActions()
actions.getFieldValue('username').then(val => console.log(val))
返回包含所有 Formily 生命周期的钩子函数,可以被监听消费
用法
import { FormEffectHooks, Form } from '@formily/react'
const {
/**
* Form LifeCycle
**/
onFormWillInit$, // 表单预初始化触发
onFormInit$, // 表单初始化触发
onFormChange$, // 表单变化时触发
onFormInputChange$, // 表单事件触发时触发,用于只监控人工操作
onFormInitialValueChange$, // 表单初始值变化时触发
onFormSubmitValidateStart$, // 表单提交时触发校验开始
onFormSubmitValidateSuccess$, // 表单提交时触发校验成功
onFormSubmitValidateFailed$, // 表单提交时触发校验失败
onFormReset$, // 表单重置时触发
onFormSubmit$, // 表单提交时触发
onFormSubmitStart$, // 表单提交开始时触发
onFormSubmitEnd$, // 表单提交结束时触发
onFormMount$, // 表单挂载时触发
onFormUnmount$, // 表单卸载时触发
onFormValidateStart$, // 表单校验开始时触发
onFormValidateEnd$, //表单校验结束时触发
onFormValuesChange$, // 表单值变化时触发
/**
* FormGraph LifeCycle
**/
onFormGraphChange$, // 表单观察者树变化时触发
/**
* Field LifeCycle
**/
onFieldWillInit$, // 字段预初始化时触发
onFieldInit$, // 字段初始化时触发
onFieldChange$, // 字段变化时触发
onFieldMount$, // 字段挂载时触发
onFieldUnmount$, // 字段卸载时触发
onFieldInputChange$, // 字段事件触发时触发,用于只监控人工操作
onFieldValueChange$, // 字段值变化时触发
onFieldInitialValueChange$ // 字段初始值变化时触发
} = FormEffectHooks
const App = () => {
return (
<Form
effects={() => {
onFormInit$().subscribe(() => {
console.log('初始化')
})
}}
>
...
</Form>
)
}
自定义 hook
签名
(type: string): Observable<TResult>
用法
import { Form, createEffectHook, createFormActions } from '@formily/react'
const actions = createFormActions()
const diyHook1$ = createEffectHook('diy1')
const diyHook2$ = createEffectHook('diy2')
const App = () => {
return (
<Form
actions={actions}
effects={() => {
diyHook1$().subscribe(payload => {
console.log('diy1 hook triggered', payload)
})
diyHook2$().subscribe(payload => {
console.log('diy2 hook triggered', payload)
})
}}
>
<button
onClick={() => {
actions.notify('diy1', { index: 1 })
}}
>
notify diy1
</button>
<button
onClick={() => {
actions.notify('diy2', { index: 2 })
}}
>
notify diy2
</button>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
整体完全继承@formily/core, 下面只列举@formily/react 的特有的 Interfaces
通过 createForm 创建出来的 Form 实例对象 API
interface IForm {
/*
* 表单提交,如果回调参数返回Promise,
* 那么整个提交流程会hold住,同时loading为true,
* 等待Promise resolve才触发表单onFormSubmitEnd事件,同时loading为false
*/
submit(
onSubmit?: (values: IFormState['values']) => any | Promise<any>
): Promise<{
validated: IFormValidateResult
payload: any //onSubmit回调函数返回值
}>
/*
* 清空错误消息,可以通过传FormPathPattern来批量或精确控制要清空的字段,
* 比如clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => void
/*
* 获取状态变化情况,主要用于在表单生命周期钩子内判断当前生命周期中有哪些状态发生了变化,
* 比如hasChanged(state,'value.aa')
*/
hasChanged(
target: IFormState | IFieldState | IVirtualFieldState,
path: FormPathPattern
): boolean
/*
* 重置表单
*/
reset(options?: {
//强制清空
forceClear?: boolean
//强制校验
validate?: boolean
//重置范围,用于批量或者精确控制要重置的字段
selector?: FormPathPattern
//是否清空默认值
clearInitialValue?: boolean
}): Promise<void | IFormValidateResult>
/*
* 校验表单
*/
validate(
path?: FormPathPattern,
options?: {
//是否悲观校验,如果当前字段遇到第一个校验错误则停止后续校验流程
first?: boolean
}
): Promise<IFormValidateResult>
/*
* 设置表单状态
*/
setFormState(
//操作回调
callback?: (state: IFormState) => any,
//是否不触发事件
silent?: boolean
): void
/*
* 获取表单状态
*/
getFormState(
//transformer
callback?: (state: IFormState) => any
): any
/*
* 设置字段状态
*/
setFieldState(
//字段路径
path: FormPathPattern,
//操作回调
callback?: (state: IFieldState) => void,
//是否不触发事件
silent?: boolean
): void
/*
* 获取字段状态
*/
getFieldState(
//字段路径
path: FormPathPattern,
//transformer
callback?: (state: IFieldState) => any
): any
/*
* 注册字段
*/
registerField(props: {
//节点路径
path?: FormPathPattern
//数据路径
name?: string
//字段值
value?: any
//字段多参值
values?: any[]
//字段初始值
initialValue?: any
//字段扩展属性
props?: any
//字段校验规则
rules?: ValidatePatternRules[]
//字段是否必填
required?: boolean
//字段是否可编辑
editable?: boolean
//字段是否走脏检查
useDirty?: boolean
//字段状态计算容器,主要用于扩展核心联动规则
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IField
/*
* 注册虚拟字段
*/
registerVirtualField(props: {
//节点路径
path?: FormPathPattern
//数据路径
name?: string
//字段扩展属性
props?: any
//字段是否走脏检查
useDirty?: boolean
//字段状态计算容器,主要用于扩展核心联动规则
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IVirtualField
/*
* 创建字段数据操作器,后面会详细解释返回的API
*/
createMutators(field: IField | FormPathPattern): IMutators
/*
* 获取表单观察者树
*/
getFormGraph(): IFormGraph
/*
* 设置表单观察者树
*/
setFormGraph(graph: IFormGraph): void
/*
* 监听表单生命周期
*/
subscribe(
callback?: ({ type, payload }: { type: string; payload: any }) => void
): number
/*
* 取消监听表单生命周期
*/
unsubscribe(id: number): void
/*
* 触发表单自定义生命周期
*/
notify: <T>(type: string, payload?: T) => void
/*
* 设置字段值
*/
setFieldValue(path?: FormPathPattern, value?: any): void
/*
* 获取字段值
*/
getFieldValue(path?: FormPathPattern): any
/*
* 设置字段初始值
*/
setFieldInitialValue(path?: FormPathPattern, value?: any): void
/*
* 获取字段初始值
*/
getFieldInitialValue(path?: FormPathPattern): any
}
通过 createMutators 创建出来的实例 API,主要用于操作字段数据
interface IMutators {
//改变字段值,多参情况,会将所有参数存在values中
change(...values: any[]): any
//获取焦点,触发active状态改变
focus(): void
//失去焦点,触发active/visited状态改变
blur(): void
//触发当前字段校验器
validate(): Promise<IFormValidateResult>
//当前字段的值是否在Form的values属性中存在
exist(index?: number | string): boolean
/**数组操作方法**/
//追加数据
push(value?: any): any[]
//弹出尾部数据
pop(): any[]
//插入数据
insert(index: number, value: any): any[]
//删除数据
remove(index: number | string): any
//头部插入
unshift(value: any): any[]
//头部弹出
shift(): any[]
//移动元素
move($from: number, $to: number): any[]
//下移
moveDown(index: number): any[]
//上移
moveUp(index: number): any[]
}
interface IFormActions {
/*
* 表单提交,如果回调参数返回Promise,
* 那么整个提交流程会hold住,同时loading为true,
* 等待Promise resolve才触发表单onFormSubmitEnd事件,同时loading为false
*/
submit(
onSubmit?: (values: IFormState['values']) => any | Promise<any>
): Promise<{
validated: IFormValidateResult
payload: any //onSubmit回调函数返回值
}>
/*
* 清空错误消息,可以通过传FormPathPattern来批量或精确控制要清空的字段,
* 比如clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => void
/*
* 获取状态变化情况,主要用于在表单生命周期钩子内判断当前生命周期中有哪些状态发生了变化,
* 比如hasChanged(state,'value.aa')
*/
hasChanged(
target: IFormState | IFieldState | IVirtualFieldState,
path: FormPathPattern
): boolean
/*
* 重置表单
*/
reset(options?: {
//强制清空
forceClear?: boolean
//强制校验
validate?: boolean
//重置范围,用于批量或者精确控制要重置的字段
selector?: FormPathPattern
//是否清空默认值
clearInitialValue?: boolean
}): Promise<void | IFormValidateResult>
/*
* 校验表单, 当校验失败时抛出异常
*/
validate(
path?: FormPathPattern,
options?: {
//是否悲观校验,如果当前字段遇到第一个校验错误则停止后续校验流程
first?: boolean
}
): Promise<IFormValidateResult>
/*
* 设置表单状态
*/
setFormState(
//操作回调
callback?: (state: IFormState) => any,
//是否不触发事件
silent?: boolean
): void
/*
* 获取表单状态
*/
getFormState(
//transformer
callback?: (state: IFormState) => any
): any
/*
* 设置字段状态
*/
setFieldState(
//字段路径
path: FormPathPattern,
//操作回调
callback?: (state: IFieldState) => void,
//是否不触发事件
silent?: boolean
): void
/*
* 获取字段状态
*/
getFieldState(
//字段路径
path: FormPathPattern,
//transformer
callback?: (state: IFieldState) => any
): any
/*
* 获取表单观察者树
*/
getFormGraph(): IFormGraph
/*
* 设置表单观察者树
*/
setFormGraph(graph: IFormGraph): void
/*
* 监听表单生命周期
*/
subscribe(
callback?: ({ type, payload }: { type: string; payload: any }) => void
): number
/*
* 取消监听表单生命周期
*/
unsubscribe(id: number): void
/*
* 触发表单自定义生命周期
*/
notify: <T>(type: string, payload?: T) => void
dispatch: <T>(type: string, payload?: T) => void
/*
* 设置字段值
*/
setFieldValue(path?: FormPathPattern, value?: any): void
/*
* 获取字段值
*/
getFieldValue(path?: FormPathPattern): any
/*
* 设置字段初始值
*/
setFieldInitialValue(path?: FormPathPattern, value?: any): void
/*
* 获取字段初始值
*/
getFieldInitialValue(path?: FormPathPattern): any
}
interface IFormAsyncActions {
/*
* 表单提交,如果回调参数返回Promise,
* 那么整个提交流程会hold住,同时loading为true,
* 等待Promise resolve才触发表单onFormSubmitEnd事件,同时loading为false
*/
submit(
onSubmit?: (values: IFormState['values']) => void | Promise<any>
): Promise<IFormSubmitResult>
/*
* 重置表单
*/
reset(options?: {
//强制清空
forceClear?: boolean
//强制校验
validate?: boolean
//重置范围,用于批量或者精确控制要重置的字段
selector?: FormPathPattern
//是否清空默认值
clearInitialValue?: boolean
}): Promise<void>
/*
* 获取状态变化情况,主要用于在表单生命周期钩子内判断当前生命周期中有哪些状态发生了变化,
* 比如hasChanged(state,'value.aa')
*/
hasChanged(target: any, path: FormPathPattern): Promise<boolean>
/*
* 清空错误消息,可以通过传FormPathPattern来批量或精确控制要清空的字段,
* 比如clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => Promise<void>
/*
* 校验表单, 当校验失败时抛出异常
*/
validate(
path?: FormPathPattern,
options?: {
//是否悲观校验,如果当前字段遇到第一个校验错误则停止后续校验流程
first?: boolean
}
): Promise<IFormValidateResult>
/*
* 设置表单状态
*/
setFormState(
//操作回调
callback?: (state: IFormState) => any,
//是否不触发事件
silent?: boolean
): Promise<void>
/*
* 获取表单状态
*/
getFormState(
//transformer
callback?: (state: IFormState) => any
): Promise<any>
/*
* 设置字段状态
*/
setFieldState(
//字段路径
path: FormPathPattern,
//操作回调
callback?: (state: IFieldState) => void,
//是否不触发事件
silent?: boolean
): Promise<void>
/*
* 获取字段状态
*/
getFieldState(
//字段路径
path: FormPathPattern,
//transformer
callback?: (state: IFieldState) => any
): Promise<void>
getFormGraph(): Promise<IFormGraph>
setFormGraph(graph: IFormGraph): Promise<void>
subscribe(callback?: FormHeartSubscriber): Promise<number>
unsubscribe(id: number): Promise<void>
notify: <T>(type: string, payload?: T) => Promise<void>
dispatch: <T>(type: string, payload?: T) => Promise<void>
setFieldValue(path?: FormPathPattern, value?: any): Promise<void>
getFieldValue(path?: FormPathPattern): Promise<any>
setFieldInitialValue(path?: FormPathPattern, value?: any): Promise<void>
getFieldInitialValue(path?: FormPathPattern): Promise<any>
}
interface IFieldState<FieldProps = any> {
/**只读属性**/
//状态名称,FieldState
displayName?: string
//数据路径
name: string
//节点路径
path: string
//是否已经初始化
initialized: boolean
//是否处于原始态,只有value===intialValues时的时候该状态为true
pristine: boolean
//是否处于合法态,只要errors长度大于0的时候valid为false
valid: boolean
//是否处于非法态,只要errors长度大于0的时候valid为true
invalid: boolean
//是否处于校验态
validating: boolean
//是否被修改,如果值发生变化,该属性为true,同时在整个字段的生命周期内都会为true
modified: boolean
//是否被触碰
touched: boolean
//是否被激活,字段触发onFocus事件的时候,它会被触发为true,触发onBlur时,为false
active: boolean
//是否访问过,字段触发onBlur事件的时候,它会被触发为true
visited: boolean
/**可写属性**/
//是否可见,注意:该状态如果为false,那么字段的值不会被提交,同时UI不会显示
visible: boolean
//是否展示,注意:该状态如果为false,那么字段的值会提交,UI不会展示,类似于表单隐藏域
display: boolean
//是否可编辑
editable: boolean
//是否处于loading状态,注意:如果字段处于异步校验时,loading为true
loading: boolean
//字段多参值,比如字段onChange触发时,给事件回调传了多参数据,那么这里会存储所有参数的值
values: any[]
//字段错误消息
errors: string[]
//字段告警消息
warnings: string[]
//字段值,与values[0]是恒定相等
value: any
//初始值
initialValue: any
//校验规则,具体类型描述参考后面文档
rules: ValidatePatternRules[]
//是否必填
required: boolean
//是否挂载
mounted: boolean
//是否卸载
unmounted: boolean
//字段扩展属性
props: FieldProps
}
虚拟 Field 核心状态
interface IVirtualFieldState<FieldProps = any> {
/**只读状态**/
//状态名称,VirtualFieldState
displayName: string
//字段数据路径
name: string
//字段节点路径
path: string
//是否已经初始化
initialized: boolean
/**可写状态**/
//是否可见,注意:该状态如果为false,UI不会显示,数据也不会提交(因为它是VirtualField)
visible: boolean
//是否展示,注意:该状态如果为false,UI不会显示,数据也不会提交(因为它是VirtualField)
display: boolean
//是否已挂载
mounted: boolean
//是否已卸载
unmounted: boolean
//字段扩展属性
props: FieldProps
}
interface IFormSpyProps {
selector?: string[] | string
reducer?: (
state: any,
action: { type: string; payload: any },
form: IForm
) => any
children?: React.ReactElement | ((api: IFormSpyAPI) => React.ReactElement)
}
interface IFieldHook {
form: IForm
state: IFieldState
props: {}
mutators: IMutators
}
interface IVirtualFieldHook {
form: IForm
state: IFieldState
props: {}
}
interface ISpyHook {
form: IForm
state: any
type: string
}
declare type SyncValidateResponse =
| null
| string
| boolean
| {
type?: 'error' | 'warning'
message: string
}
declare type AsyncValidateResponse = Promise<SyncValidateResponse>
export declare type ValidateResponse =
| SyncValidateResponse
| AsyncValidateResponse
type InternalFormats =
| 'url'
| 'email'
| 'ipv6'
| 'ipv4'
| 'idcard'
| 'taodomain'
| 'qq'
| 'phone'
| 'money'
| 'zh'
| 'date'
| 'zip'
| string
declare type CustomValidator = (
value: any,
description?: ValidateDescription
) => ValidateResponse
interface ValidateDescription {
// 内置校验规则,参考string内置校验规则
format?: InternalFormats
// 自定义校验规则
validator?: CustomValidator
// 是否必填
required?: boolean
// 匹配规则
pattern?: RegExp | string
// 最大长度
max?: number
// 最大值(大于)
maximum?: number
// 最大值(大于等于)
exclusiveMaximum?: number
// 最小值(小于等于)
exclusiveMinimum?: number
// 最小值(小于)
minimum?: number
// 最小长度
min?: number
// 长度
len?: number
// 空格
whitespace?: boolean
// 是否包含在枚举列表中
enum?: any[]
// 错误信息
message?: string
[key: string]: any
}
declare type ValidateArrayRules = Array<
InternalFormats | CustomValidator | ValidateDescription
>
declare type ValidatePatternRules =
| InternalFormats
| CustomValidator
| ValidateDescription
| ValidateArrayRules
interface IFieldAPI {
state: IFieldState
form: IForm
props: {}
mutators: IMutators
}
interface IVirtualFieldAPI {
state: IFieldState
form: IForm
props: {}
}