English | 简体中文
@uform/react is based on
react
and @uform/core is already built in. It provide API to manuplate form state and components for rendering support. it mainly includes:
- Form
- Field
- VirtualField
- FormaSpy
- FormProvider
- FormConsumer(deprecated,pls using FormSpy)
- createFormActions (create sync API to manuplate form state)
- createAsyncFormActions (create async API to manuplate form state)
- FormEffectHooks (LifeCycles Hook)
npm install --save @uform/react
Usage
- 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 '@uform/react'
const { onFormInit$, onFormInputChange$, onFieldInputChange$ } = FormEffectHooks
const actions = createFormActions()
const App = () => {
return (
<Form
actions={actions}
effects={() => {
onFormInit$().subscribe(() => {
console.log('initialized')
})
onFieldInputChange$().subscribe(state => {
console.log('field change', 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'))
Example:Show you how to bind the <input>
field and subsequent examples are based on this field
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>
)
Example:required validation + error type validation + warning type validation + custom validation The type of rules is ValidatePatternRules which is InternalFormats | CustomValidator | ValidateDescription | ValidateArrayRules
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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
? { type: 'error', message: 'age is required' }
: undefined
]}
/>
<h5>warning type validation</h5>
<span>gender</span>
<InputField
name="gender"
rules={[
val =>
!val
? { 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
? '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'))
Example:User info user(username, age)
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Example:Id list
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Example:User list
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Example: see how display
与 visible
affect values
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes, FormSpy } from '@uform/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 (<div>
{JSON.stringify(form.getFormState(state => state.values))}
</div>)
}}
</FormSpy>
<button
onClick={() =>
console.log(actions.getFormState(state => state.values))
}
>
print
</button>
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Example:Show/hide field and modified props/value by using effects
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@uform/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'))
Example:Change dataSource in select asynchronously by effects
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@uform/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'))
Example:validation when form mounted and re-trigger validation when field change
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@uform/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'))
Example:See how ArrayField communicate with other field by using effects
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@uform/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
})
})
}}
>
<div>
<Field name="trigger" label="show/hide username">
{({ state, mutators }) => {
return <input type="checkbox" onChange={mutators.change} checked={state.value ? 'checked' : undefined } />
}}
</Field>
</div>
<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'))
Make your own reusable effects.
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormEffectHooks } from '@uform/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'))
Example:Combo value of username and age. Check FormSpy for more inforation.
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Dictionary
--app
|---components
|---customForm
Example:Cross-file consumption form state, Check FormProvider and FormSpy for more infomation.
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy, FormProvider } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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 '@uform/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 '@uform/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>
Props
interface IFormProps {
// Form value
value?: any
defaultValue?: any // Form initial value
initialValues?: any
// formAPI
actions?: IFormActions | IFormAsyncActions
// effect
effects?: IFormEffect<any, IFormActions | IFormAsyncActions>
// IForm instance
form?: IForm // Form change event callback
onChange?: (values: Value) => void // Form submission event callback
onSubmit?: (values: Value) => void | Promise<Value> // Form reset event callback
onReset?: () => void // Form verification failure event callback
onValidateFailed?: (valideted: IFormValidateResult) => void
children?: React.ReactElement | ((form: IForm) => React.ReactElement)
// Whether to use the dirty check, the default will go immer accurate update
useDirty?: boolean
// Is it editable, overall control in the Form dimension
editable?: boolean
// Whether to go pessimistic check, stop the subsequent check when the first check fails
validateFirst?: boolean
}
<Field>
Props
interface IFieldStateUIProps {
// Node path
path?: FormPathPattern // Node path
nodePath?: FormPathPattern // Data path
dataPath?: FormPathPattern // Data path
name?: string // Field value, is equal to values[0]
value?: any // Field multi-parameter value, such as when the field onChange trigger, the event callback passed multi-parameter data, then the value of all parameters will be stored here
values?: any[] // Initial value
initialValue?: any // field extension properties
visible?: boolean //Field initial visible status(Whether the data and style is visible)
display?: boolean //Field initial display status(Whether the style is visible)
props?: FieldProps // Check the rules, the specific type description refers to the following documents
rules?: ValidatePatternRules[] // Is it required?
required?: boolean // Is it editable?
editable?: boolean // Whether to use the dirty check, the default will go immer accurate update
useDirty?: boolean
// Field state calculation container, mainly used to extend the core linkage rules
computeState?: (draft: IFieldState, prevState: IFieldState) => void
// type of trigger validation
triggerType?: 'onChange' | 'onBlur'
// get value from browser event(eg. e.target.value)
getValueFromEvent?: (...args: any[]) => any
children?: React.ReactElement | ((api: IFieldAPI) => React.ReactElement)
}
Usage
Example:All type of field
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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>
Props
interface IVirtualFieldProps {
// Node path
path?: FormPathPattern // Node path
nodePath?: FormPathPattern // Data path
dataPath?: FormPathPattern // Data path
visible?: boolean //Field initial visible status(Whether the data and style is visible)
display?: boolean //Field initial display status(Whether the style is visible)
name?: string // Form extension properties
props?: FieldProps // Whether to use the dirty check, the default will go immer accurate update
useDirty?: boolean
// Field state calculation container, mainly used to extend the core linkage rules
computeState?: (draft: IFieldState, prevState: IFieldState) => void
children?: React.ReactElement | ((api: IFieldAPI) => React.ReactElement)
}
Usage
Example:Setting <Layout>
size from 100x100 to 200x200
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, VirtualField } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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>
Props
interface IFormSpyProps {
// selector, eg: [ LifeCycleTypes.ON_FORM_SUBMIT_START, LifeCycleTypes.ON_FORM_SUBMIT_END ]
selector?: string[] | string
// reducer
reducer?: (
state: any,
action: { type: string; payload: any },
form: IForm
) => any
children?: React.ReactElement | ((api: IFormSpyAPI) => React.ReactElement)
}
Usage
Example1: Form state change counter
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy, LifeCycleTypes } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Example2:Combo
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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'))
Used with FormSpy, often used in Cross-file consumption form state
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormSpy, FormProvider } from '@uform/react'
const actions = createFormActions()
const InputField = props => (
<Field {...props}>
{({ state, mutators }) => (
<React.Fragment>
<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>
Props
interface IFormConsumerProps {
// eg.[ LifeCycleTypes.ON_FORM_SUBMIT_START, LifeCycleTypes.ON_FORM_SUBMIT_END ]
selector?: string[] | string
children?:
| React.ReactElement
| ((api: IFormConsumerAPI) => React.ReactElement)
}
Implement local effects by using useFormEffects. Same effect as the example of Linkage Note: The life cycle of the listener starts from
ON_FORM_MOUNT
Signature
(effects: IFormEffect): void
import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, useFormEffects, LifeCycleTypes } from '@uform/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 '@uform/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'))
Manage state of custom field by using
useFieldState
Signature
(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 '@uform/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 ref = useRef(current)
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
})
})
})
})
ref.current = current
const btns = dataSource.map((item, idx) => {
console.log('current', current, ref.current)
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'))
get IForm instance
Signature
type useForm = <
Value = any,
DefaultValue = any,
EffectPayload = any,
EffectAction = any
>(
props: IFormProps<Value, DefaultValue, EffectPayload, EffectAction>
) => IForm
Usage
import { useForm } from '@uform/react'
const FormFragment = () => {
const form = useForm()
return <div>{form.getFieldValue('username')}</div>
}
get IFieldHook instance
Signature
type useField = (options: IFieldStateUIProps): IFieldHook
Usage
import { useField } from '@uform/react'
const FormFragment = (props) => {
const {
form,
state,
props: fieldProps,
mutators
} = useField({ name: 'username' })
return <input {...fieldProps} {...props} value={state.value} onChange={mutators.change} />
}
get IVirtualFieldHook instance
Signature
type UseVirtualField = (options: IVirtualFieldStateProps): IVirtualFieldHook
Usage
import { UseVirtualField } from '@uform/react'
const FormFragment = (props) => {
const {
form,
state,
props: fieldProps,
} = UseVirtualField({ name: 'username' })
return <div style={{ width: fieldProps.width, height: fieldProps.height }}>
{props.children}
</div>
}
get ISpyHook instance. Same effect as the first example of FormSpy.
Signature
type useFormSpy = (props: IFormSpyProps): ISpyHook
Usage
import { useFormSpy, LifeCycleTypes } from '@uform/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>
}
The API is fully inherited from @uform/core. The specific API of @uform/react is listed below.
Return IFormActions
Signature
createFormActions(): IFormActions
Usage
import { createFormActions } from '@uform/react'
const actions = createFormActions()
console.log(actions.getFieldValue('username'))
Return IFormAsyncActions
Signature
createAsyncFormActions(): IFormAsyncActions
Usage
import { createAsyncFormActions } from '@uform/react'
const actions = createAsyncFormActions()
actions.getFieldValue('username').then(val => console.log(val))
Return all @uform/core lifeCycles hook which can be subscribe
Usage
import { FormEffectHooks, Form } from '@uform/react'
const {
/**
* Form LifeCycle
**/
// Form pre-initialization trigger
onFormWillInit$,
// Form initialization trigger
onFormInit$,
// Triggered when the form changes
onFormChange$,
// Triggered when the form event is triggered, used to monitor only manual operations
onFormInputChange$,
// Trigger when the form initial value changes
onFormInitialValueChange$,
// Triggered when the form submission within validate
onFormSubmitValidateStart$,
// Triggered when the form submission within validate and successs
onFormSubmitValidateSuccess$,
// Triggered when the form submission within validate and faield
onFormSubmitValidateFailed$,
// Triggered when the form is reset
onFormReset$,
// Triggered when the form is submitted
onFormSubmit$,
// Triggered when the form submission starts
onFormSubmitStart$,
// Triggered when the form submission ends
onFormSubmitEnd$,
// Triggered when the form is mounted
onFormMount$,
// Triggered when the form is unloaded
onFormUnmount$,
// Triggered when form validation begins
onFormValidateStart$,
// Triggered when the form validation ends
onFormValidateEnd$,
// Trigger when the form initial value changes
onFormValuesChange$,
/**
* FormGraph LifeCycle
**/
// Triggered when the form observer tree changes
onFormGraphChange$,
/**
* Field LifeCycle
**/
// Triggered when pre-initialized
onFieldWillInit$,
// Triggered when the field is initialized
onFieldInit$,
// Triggered when the field changes
onFieldChange$,
// Triggered when the field is mounted
onFieldMount$,
// Trigger when the field is unloaded
onFieldUnmount$,
// Triggered when the field event is triggered, used to monitor only manual operations
onFieldInputChange$,
// Triggered when the field value changes
onFieldValueChange$,
// Trigger when the initial value of the field changes
onFieldInitialValueChange$
} = FormEffectHooks
const App = () => {
return (
<Form
effects={() => {
onFormInit$().subscribe(() => {
console.log('initialized')
})
}}
>
...
</Form>
)
}
Custom your own hook by this api
Signature
(type: string): Observable<TResult>
Usage
import { Form, createEffectHook, createFormActions } from '@uform/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'))
The Interfaces is fully inherited from @uform/core. The specific Interfaces of @uform/react is listed below.
Form instance object API created by using createForm
interface IForm {
/*
* Form submission, if the callback parameter returns Promise,
* Then the entire submission process will hold and load is true.
* Wait for Promise resolve to trigger the form onFormSubmitEnd event while loading is false
*/
submit(
onSubmit?: (values: IFormState['values']) => any | Promise<any>
): Promise<{
Validated: IFormValidateResult
Payload: any //onSubmit callback function return value
}>
/*
* Clear the error message, you can pass the FormPathPattern to batch or precise control of the field to be cleared.
* For example, clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => void
/*
* Get status changes, mainly used to determine which states in the current life cycle have changed in the form lifecycle hook.
* For example, hasChanged(state,'value.aa')
*/
hasChanged(target: IFormState | IFieldState | IVirtualFieldState, path: FormPathPattern): boolean
/*
* Reset form
*/
reset(options?: {
// Forced to empty
forceClear?: boolean
// Forced check
validate?: boolean
// Reset range for batch or precise control of the field to be reset
selector?: FormPathPattern
}): Promise<void | IFormValidateResult>
/*
* Validation form
*/
validate(path?: FormPathPattern, options?: {
// Is it pessimistic check, if the current field encounters the first verification error, stop the subsequent verification process
first?:boolean
}): Promise<IFormValidateResult>
/*
* Set the form status
*/
setFormState(
// Operation callback
callback?: (state: IFormState) => any,
// No trigger the event
silent?: boolean
): void
/*
* Get form status
*/
getFormState(
//transformer
callback?: (state: IFormState) => any
): any
/*
* Set the field status
*/
setFieldState(
// Field path
path: FormPathPattern,
// Operation callback
callback?: (state: IFieldState) => void,
// No trigger the event
silent?: boolean
): void
/*
* Get the field status
*/
getFieldState(
// Field path
path: FormPathPattern,
// Transformer
callback?: (state: IFieldState) => any
): any
/*
* Registration field
*/
registerField(props: {
// Node path
path?: FormPathPattern
// Data path
name?: string
// Field value
value?: any
// Field multi-value
values?: any[]
// Field initial value
initialValue?: any
// Field extension properties
props?: any
// Field check rule
rules?: ValidatePatternRules[]
// Field is required
required?: boolean
// Is the field editable?
editable?: boolean
// Whether the field is dirty check
useDirty?: boolean
// Field state calculation container, mainly used to extend the core linkage rules
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IField
/*
* Register virtual fields
*/
registerVirtualField(props: {
// Node path
path?: FormPathPattern
// Data path
name?: string
// Field extension properties
props?: any
// Whether the field is dirty check
useDirty?: boolean
// Field state calculation container, mainly used to extend the core linkage rules
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IVirtualField
/*
* Create a field data operator, which will explain the returned API in detail later.
*/
createMutators(field: IField): IMutators
/*
* Get the form observer tree
*/
getFormGraph(): IFormGraph
/*
* Set the form observer tree
*/
setFormGraph(graph: IFormGraph): void
/*
* Listen to the form life cycle
*/
subscribe(callback?: ({
type,
payload
}: {
type: string
payload: any
}) => void): number
/*
* Cancel the listening form life cycle
*/
unsubscribe(id: number): void
/*
* Trigger form custom life cycle
*/
notify: <T>(type: string, payload?: T) => void
/*
* Set the field value
*/
setFieldValue(path?: FormPathPattern, value?: any): void
/*
* Get the field value
*/
getFieldValue(path?: FormPathPattern): any
/*
* Set the initial value of the field
*/
setFieldInitialValue(path?: FormPathPattern, value?: any): void
/*
* Get the initial value of the field
*/
getFieldInitialValue(path?: FormPathPattern): any
}
The instance API created by crewikiutators is mainly used to operate field data.
interface IMutators {
// Changing the field value and multi parameter condition will store all parameters in values
change(...values: any[]): any
// Get focus, trigger active state change
focus(): void
// Lose focus, trigger active / visited status change
blur (): void
// Trigger current field verifier
validate(): Promise<IFormValidateResult>
// Whether the value of the current field exists in the values property of form
exist (index?: number | string): Boolean
/**Array operation method**/
// Append data
push(value?: any): any[]
// Pop up tail data
pop (): any[]
// Insert data
insert(index: number, value: any): any[]
// Delete data
remove(index: number | string): any
// Head insertion
unshift(value: any): any[]
// Head ejection
shift(): any[]
// Move element
move($from: number, $to: number): any[]
// Move down
moveDown(index: number): any[]
// Move up
moveUp(index: number): any[]
}
interface IFormActions {
/*
* Form submission, if the callback parameter returns Promise,
* Then the entire submission process will hold and load is true.
* Wait for Promise resolve to trigger the form onFormSubmitEnd event while loading is false
*/
submit(
onSubmit?: (values: IFormState['values']) => any | Promise<any>
): Promise<{
Validated: IFormValidateResult
Payload: any //onSubmit callback function return value
}>
/*
* Clear the error message, you can pass the FormPathPattern to batch or precise control of the field to be cleared.
* For example, clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => void
/*
* Get status changes, mainly used to determine which states in the current life cycle have changed in the form lifecycle hook.
* For example, hasChanged(state,'value.aa')
*/
hasChanged(
target: IFormState | IFieldState | IVirtualFieldState,
path: FormPathPattern
): boolean
/*
* Reset form
*/
reset(options?: {
// Forced to empty
forceClear?: boolean // Forced check
validate?: boolean // Reset range for batch or precise control of the field to be reset
selector?: FormPathPattern
}): Promise<void | IFormValidateResult>
/*
* Validation form, throw IFormValidateResult when validation fails
*/
validate(
path?: FormPathPattern,
options?: {
// Is it pessimistic check, if the current field encounters the first verification error, stop the subsequent verification process
first?: boolean
}
): Promise<IFormValidateResult>
/*
* Set the form status
*/
setFormState( // Operation callback
callback?: (state: IFormState) => any, // No trigger the event
silent?: boolean
): void
/*
* Get form status
*/
getFormState( //transformer
callback?: (state: IFormState) => any
): any
/*
* Set the field status
*/
setFieldState( // Field path
path: FormPathPattern, // Operation callback
callback?: (state: IFieldState) => void, // No trigger the event
silent?: boolean
): void
/*
* Get the field status
*/
getFieldState( // Field path
path: FormPathPattern, // Transformer
callback?: (state: IFieldState) => any
): any
/*
* Get the form observer tree
*/
getFormGraph(): IFormGraph
/*
* Set the form observer tree
*/
setFormGraph(graph: IFormGraph): void
/*
* Listen to the form life cycle
*/
subscribe(
callback?: ({ type, payload }: { type: string; payload: any }) => void
): number
/*
* Cancel the listening form life cycle
*/
unsubscribe(id: number): void
/*
* Trigger form custom life cycle
*/
notify: <T>(type: string, payload?: T) => void
dispatch: <T>(type: string, payload?: T) => void
/*
* Set the field value
*/
setFieldValue(path?: FormPathPattern, value?: any): void
/*
* Get the field value
*/
getFieldValue(path?: FormPathPattern): any
/*
* Set the initial value of the field
*/
setFieldInitialValue(path?: FormPathPattern, value?: any): void
/*
* Get the initial value of the field
*/
getFieldInitialValue(path?: FormPathPattern): any
}
interface IFormAsyncActions {
/*
* Form submission, if the callback parameter returns Promise,
* Then the entire submission process will hold and load is true.
* Wait for Promise resolve to trigger the form onFormSubmitEnd event while loading is false
*/
submit(
onSubmit?: (values: IFormState['values']) => void | Promise<any>
): Promise<IFormSubmitResult>
/*
* Reset form
*/
reset(options?: IFormResetOptions): Promise<void>
/*
* Get status changes, mainly used to determine which states in the current life cycle have changed in the form lifecycle hook.
* For example, hasChanged(state,'value.aa')
*/
hasChanged(target: any, path: FormPathPattern): Promise<boolean>
/*
* Clear the error message, you can pass the FormPathPattern to batch or precise control of the field to be cleared.
* For example, clearErrors("*(aa,bb,cc)")
*/
clearErrors: (pattern?: FormPathPattern) => Promise<void>
/*
* Validation form, throw IFormValidateResult when validation fails
*/
validate(
path?: FormPathPattern,
options?: {
// Is it pessimistic check, if the current field encounters the first verification error, stop the subsequent verification process
first?: boolean
}
): Promise<IFormValidateResult>
/*
* Set the form state
*/
setFormState(
// Operation callback
callback?: (state: IFormState) => any,
// No trigger the event
silent?: boolean
): Promise<void>
/*
* Get form state
*/
getFormState(
//transformer
callback?: (state: IFormState) => any
): Promise<any>
/*
* Set the field state
*/
setFieldState(
// Field path
path: FormPathPattern,
// Operation callback
callback?: (state: IFieldState) => void,
// No trigger the event
silent?: boolean
): Promise<void>
/*
* Get the field state
*/
getFieldState(
// Field path
path: FormPathPattern,
//transformer
callback?: (state: IFieldState) => any
): Promise<void>
/*
* Get the form observer tree
*/
getFormGraph(): Promise<IFormGraph>
/*
* Set the form observer tree
*/
setFormGraph(graph: IFormGraph): Promise<void>
/*
* Listen to the form life cycle
*/
subscribe(callback?: FormHeartSubscriber): Promise<number>
/*
* Cancel the listening form life cycle
*/
unsubscribe(id: number): Promise<void>
/*
* Trigger form custom life cycle
*/
notify: <T>(type: string, payload?: T) => Promise<void>
dispatch: <T>(type: string, payload?: T) => Promise<void>
/*
* Set the field value
*/
setFieldValue(path?: FormPathPattern, value?: any): Promise<void>
/*
* Get the field value
*/
getFieldValue(path?: FormPathPattern): Promise<any>
/*
* Set the initial value of the field
*/
setFieldInitialValue(path?: FormPathPattern, value?: any): Promise<void>
/*
* Get the initial value of the field
*/
getFieldInitialValue(path?: FormPathPattern): Promise<any>
}
interface IFieldState<FieldProps = any> {
/**Read-only attribute**/
// State name, FieldState
displayName?: string // Data path
name: string // Node path
path: string // Has been initialized
initialized: boolean // Is it in the original state, the state is true only when value===intialValues
pristine: boolean // Is it in a legal state, as long as the error length is greater than 0, the valid is false
valid: boolean // Is it illegal, as long as the error length is greater than 0, the valid is true
invalid: boolean // Is it in check state?
validating: boolean // Is it modified, if the value changes, the property is true, and will be true throughout the life of the field
modified: boolean // Is it touched?
touched: boolean // Is it activated, when the field triggers the onFocus event, it will be triggered to true, when onBlur is triggered, it is false
active: boolean // Have you ever visited, when the field triggers the onBlur event, it will be triggered to true
visited: boolean /** writable property**/ // Is it visible, note: if the state is false, then the value of the field will not be submitted, and the UI will not display
visible: boolean // Whether to show, note: if the state is false, then the value of the field will be submitted, the UI will not display, similar to the form hidden field
display: boolean // Is it editable?
editable: boolean // Is it in the loading state, note: if the field is in asynchronous verification, loading is true
loading: boolean // Field multi-parameter value, such as when the field onChange trigger, the event callback passed multi-parameter data, then the value of all parameters will be stored here
values: any[] // Field error message
errors: string[] // Field alert message
warnings: string[] // Field value, is equal to values[0]
value: any // Initial value
initialValue: any // Check the rules, the specific type description refers to the following documents
rules: ValidatePatternRules[] // Is it required?
required: boolean // Whether to mount
mounted: boolean // Whether to uninstall
unmounted: boolean // field extension properties
props: FieldProps
}
interface IVirtualFieldState<FieldProps = any> {
/**Read-only status**/
// State name, VirtualFieldState
displayName: string // Field data path
name: string // Field node path
path: string // Has been initialized
initialized: boolean /** writable status**/ // Is it visible, note: if the state is false, the UI will not be displayed, the data will not be submitted (because it is a VirtualField)
visible: boolean // Whether to show, note: if the state is false, the UI will not display, the data will not be submitted (because it is VirtualField)
display: boolean // Is it mounted?
mounted: boolean // Has been uninstalled
unmounted: boolean // field extension properties
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: IFieldState
props: {}
mutators: IMutators
}
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,
rescription?: ValidateDescription
) => ValidateResponse
interface ValidateDescription {
// built-in rules,ref: string rules
format?: InternalFormats
// custom validation
validator?: CustomValidator
// required
required?: boolean
// pattern
pattern?: RegExp | string
// max length
max?: number
// maximum
maximum?: number
// exclusiveMaximum
exclusiveMaximum?: number
// exclusiveMinimum
exclusiveMinimum?: number
// minimum
minimum?: number
// min
min?: number
// length
len?: number
// whitespace
whitespace?: boolean
// enum
enum?: any[]
// error message
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: {}
}