npm install --save @uform/next
- Quick-Start
- Components
- Form List
- Layout Components
- Type of SchemaMarkupField
- Hook
- API
- Interfaces
ISchema
IFormActions
IFormAsyncActions
ButtonProps
CardProps
ICompatItemProps
IFieldState
ISchemaFieldComponentProps
ISchemaVirtualFieldComponentProps
ISchemaFieldWrapper
ISchemaFieldComponent
ISchemaVirtualFieldComponent
ISchemaFormRegistry
INextSchemaFieldProps
IPreviewTextProps
IMutators
IFieldProps
IConnectOptions
ISpyHook
Example:develop with JSX
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import { Button } from '@alifd/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="radio"
enum={['1', '2', '3', '4']}
title="Radio"
name="radio"
/>
<Field
type="string"
enum={['1', '2', '3', '4']}
required
title="Select"
name="select"
/>
<Field
type="checkbox"
enum={['1', '2', '3', '4']}
required
title="Checkbox"
name="checkbox"
/>
<Field
type="string"
title="TextArea"
name="textarea"
x-component="textarea"
/>
<Field type="number" title="number" name="number" />
<Field type="boolean" title="boolean" name="boolean" />
<Field type="date" title="date" name="date" />
<Field
type="daterange"
title="daterange"
default={['2018-12-19', '2018-12-19']}
name="daterange"
/>
<Field type="year" title="year" name="year" />
<Field type="time" title="time" name="time" />
<Field
type="upload"
title="upload(card)"
name="upload"
x-props={{ listType: 'card' }}
/>
<Field
type="upload"
title="upload(dragge)"
name="upload2"
x-props={{ listType: 'dragger' }}
/>
<Field
type="upload"
title="upload(text)"
name="upload3"
x-props={{ listType: 'text' }}
/>
<Field
type="range"
title="range"
name="range"
x-props={{ min: 0, max: 1024, marks: [0, 1024] }}
/>
<Field
type="transfer"
enum={[{ value: 1, label: 'opt1' }, { value: 2, label: 'opt2' }]}
title="transfer"
name="transfer"
/>
<Field type="rating" title="rating" name="rating" />
<FormButtonGroup offset={7} sticky>
<Submit />
<Reset />
<Button
onClick={() => {
actions.setFieldState('upload', state => {
state.value = [
{
downloadURL:
'//img.alicdn.com/tfs/TB1n8jfr1uSBuNjy1XcXXcYjFXa-200-200.png',
imgURL:
'//img.alicdn.com/tfs/TB1n8jfr1uSBuNjy1XcXXcYjFXa-200-200.png',
name: 'doc.svg'
}
]
})
}}
>
Upload
</Button>
<Button
onClick={() => {
actions.setFormState(state => {
state.values = {
radio: '4',
checkbox: ['2', '3']
}
})
}}
>
Set Value
</Button>
</FormButtonGroup>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Example:develop with JSON Schema
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import { Button } from '@alifd/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
const schema = {
type: 'object',
properties: {
radio: {
type: 'radio',
enum: ['1', '2', '3', '4'],
title: 'Radio'
},
select: {
type: 'string',
enum: ['1', '2', '3', '4'],
title: 'Select',
required: true
},
checkbox: {
type: 'checkbox',
enum: ['1', '2', '3', '4'],
title: 'Checkbox',
required: true
},
textarea: {
type: 'string',
'x-component': 'textarea',
title: 'TextArea'
},
number: {
type: 'number',
title: 'number'
},
boolean: {
type: 'boolean',
title: 'boolean'
},
date: {
type: 'date',
title: 'date'
},
daterange: {
type: 'daterange',
default: ['2018-12-19', '2018-12-19'],
title: 'daterange'
},
year: {
type: 'year',
title: 'year'
},
time: {
type: 'time',
title: 'time'
},
upload: {
type: 'upload',
'x-props': {
listType: 'card'
},
title: 'upload(card)'
},
upload2: {
type: 'upload',
'x-props': {
listType: 'dragger'
},
title: 'uplaod(dragger)'
},
upload3: {
type: 'upload',
'x-props': {
listType: 'text'
},
title: 'upload(text)'
},
range: {
type: 'range',
'x-props': {
min: 0,
max: 1024,
marks: [0, 1024]
},
title: 'range'
},
transfer: {
type: 'transfer',
enum: [
{
value: 1,
label: 'opt1'
},
{
value: 2,
label: 'opt2'
}
],
title: 'transfer'
},
rating: {
type: 'rating',
title: 'rating'
},
layout_btb_group: {
type: 'object',
'x-component': 'button-group',
'x-component-props': {
offset:7,
sticky: true,
},
properties: {
submit_btn: {
type: 'object',
'x-component': 'submit',
'x-component-props': {
children: 'Submit',
},
},
reset_btn: {
type: 'object',
'x-component': 'reset',
'x-component-props': {
children: 'Reset',
},
},
}
},
}
}
return <SchemaForm actions={actions} schema={schema} />
}
ReactDOM.render(<App />, document.getElementById('root'))
Base on <SchemaMarkupForm/>
of @uform/react-schema-renderer. Recommended for production environments.
interface INextSchemaFormProps {
// render by schema
schema?: ISchema;
fields?: ISchemaFormRegistry['fields'];
virtualFields?: ISchemaFormRegistry['virtualFields'];
// pre-registered Form Component
formComponent?: ISchemaFormRegistry['formComponent'];
// pre-registered FormItem Component
formItemComponent?: ISchemaFormRegistry['formItemComponent'];
// label column settiing
labelCol?: number | { span: number; offset?: number }
// FormItem column settiing
wrapperCol?: number | { span: number; offset?: number }
// custom placeholder when preivew
previewPlaceholder?: string | ((props: IPreviewTextProps) => string);
// prefix
prefix?: string;
// is it inline
inline?: boolean;
// The size of a single Item is customized, and takes precedence over the size of the Form, and when a component is used with an Item, the component itself does not set the size property.
size?: 'large' | 'medium' | 'small';
// position of label
labelAlign?: 'top' | 'left' | 'inset';
// aligment of label
labelTextAlign?: 'left' | 'right';
// labelCol of FormItem
labelCol?: {};
// wrapperCol of FormItem
wrapperCol?: {};
children?: any;
className?: string;
style?: React.CSSProperties;
// type of component
component?: string | (() => void);
// form state value
value?: Value;
// form state defaultValue
defaultValue?: DefaultValue;
// form state initialValues
initialValues?: DefaultValue;
// FormActions instance
actions?: FormActions;
// IFormEffect instance
effects?: IFormEffect<FormEffectPayload, FormActions>;
// form instance
form?: IForm;
// Form change event callback
onChange?: (values: Value) => void;
// triggered by `htmlType="submit"` or actions.submit时
onSubmit?: (values: Value) => void | Promise<Value>;
// triggered by <Reset/> or actions.reset
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 | ((name: string) => boolean);
// Whether to go pessimistic check, stop the subsequent check when the first check fails
validateFirst?: boolean;
}
Usage
Example1: Sync value of a and a-mirror
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
registerFormField,
Field,
connect,
createFormActions
} from '@uform/next'
const actions = createFormActions()
ReactDOM.render(
<SchemaForm actions={actions} effects={($)=>{
$('onFieldChange','a').subscribe((fieldState)=>{
actions.setFieldState('a-mirror',state=>{
state.value = fieldState.value
})
})
}}>
<Field type="string" name="a" title="a"/>
<Field type="string" name="a-mirror" title="a-mirror"/>
</SchemaForm>,
document.getElementById('root')
)
Example:Layout
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
Field,
createFormActions,
FormLayout,
FormButtonGroup,
Submit,
Reset,
} from '@uform/next'
const actions = createFormActions()
ReactDOM.render(
<div>
<h5>Basic Layout</h5>
<SchemaForm>
<FormLayout labelCol={8} wrapperCol={6}>
<Field name="aaa" type="string" title="field1" />
<Field name="bbb" type="number" title="field2" />
<Field name="ccc" type="date" title="field3" />
</FormLayout>
<FormButtonGroup offset={8}>
<Submit>Submit</Submit> <Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
<h5>Inline Layout</h5>
<SchemaForm inline>
<Field name="aaa" type="string" title="field1" />
<Field name="bbb" type="number" title="field2" />
<Field name="ccc" type="date" title="field3" />
<FormButtonGroup>
<Submit>Submit</Submit> <Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
<h5>editable = false</h5>
<SchemaForm inline editable={false}>
<Field name="aaa" type="string" title="field1" />
<Field name="bbb" type="number" title="field2" />
<Field name="ccc" type="date" title="field3" />
<FormButtonGroup>
<Submit>Submit</Submit> <Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
</div>,
document.getElementById('root')
)
Core components of @uform/next, used to describe form fields
interface IMarkupSchemaFieldProps {
name?: string
/** base json schema spec**/
title?: SchemaMessage
description?: SchemaMessage
default?: any
readOnly?: boolean
writeOnly?: boolean
type?: 'string' | 'object' | 'array' | 'number' | string
enum?: Array<string | number | { label: SchemaMessage; value: any }>
const?: any
multipleOf?: number
maximum?: number
exclusiveMaximum?: number
minimum?: number
exclusiveMinimum?: number
maxLength?: number
minLength?: number
pattern?: string | RegExp
maxItems?: number
minItems?: number
uniqueItems?: boolean
maxProperties?: number
minProperties?: number
required?: string[] | boolean
format?: string
/** nested json schema spec **/
properties?: {
[key: string]: ISchema
}
items?: ISchema | ISchema[]
additionalItems?: ISchema
patternProperties?: {
[key: string]: ISchema
}
additionalProperties?: ISchema
/** extend json schema specs */
editable?: boolean
visible?: boolean
display?: boolean
['x-props']?: { [name: string]: any }
['x-index']?: number
['x-rules']?: ValidatePatternRules
['x-component']?: string
['x-component-props']?: { [name: string]: any }
['x-render']?: <T = ISchemaFieldComponentProps>(
props: T & {
renderComponent: () => React.ReactElement
}
) => React.ReactElement
['x-effect']?: (
dispatch: (type: string, payload: any) => void,
option?: object
) => { [key: string]: any }
}
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
FormSlot,
Field,
createFormActions,
FormLayout,
FormButtonGroup,
Submit,
Reset,
} from '@uform/next'
const actions = createFormActions()
ReactDOM.render(
<SchemaForm>
<FormSlot><div>required</div></FormSlot>
<Field name="a" required type="string" title="field1" />
<FormSlot><div>description</div></FormSlot>
<Field name="b" description="description" type="string" title="field1" />
<FormSlot><div>default value</div></FormSlot>
<Field name="c" default={10} type="string" title="field1" />
<FormSlot><div>readOnly</div></FormSlot>
<Field name="d" readOnly default={10} type="string" title="field1" />
<FormSlot><div>visible = false</div></FormSlot>
<Field name="e" visible={false} default={10} type="string" title="field1" />
<FormSlot><div>display = false</div></FormSlot>
<Field name="f" visible={false} default={10} type="string" title="field1" />
<FormSlot><div>editable = false</div></FormSlot>
<Field name="g" editable={false} default={10} type="string" title="field1" />
</SchemaForm>,
document.getElementById('root')
)
Props of
<Submit/>
interface ISubmitProps {
/** reset pops **/
onSubmit?: ISchemaFormProps['onSubmit']
showLoading?: boolean
/** nextBtnProps **/
// type of btn
type?: 'primary' | 'secondary' | 'normal'
// size of btn
size?: 'small' | 'medium' | 'large'
// size of Icon
iconSize?: 'xxs' | 'xs' | 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl'
// type of button when component = 'button'
htmlType?: 'submit' | 'reset' | 'button'
// typeof btn
component?: 'button' | 'a'
// Set the loading state of the button
loading?: boolean
// Whether it is a ghost button
ghost?: true | false | 'light' | 'dark'
// Whether it is a text button
text?: boolean
// Whether it is a warning button
warning?: boolean
// Whether it is disabled
disabled?: boolean
// Callback for button click
onClick?: (e: {}) => void
// Valid when Button component is set to 'a', which represents the URL of the linked page
href?: string
// Valid when Button component is set to 'a', which represents the way of open the linked document
target?: string
}
Props of
<Reset/>
interface IResetProps {
/** reset pops **/
forceClear?: boolean
validate?: boolean
/** nextBtnProps **/
// type of btn
type?: 'primary' | 'secondary' | 'normal'
// size of btn
size?: 'small' | 'medium' | 'large'
// size of Icon
iconSize?: 'xxs' | 'xs' | 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl'
// type of button when component = 'button'
htmlType?: 'submit' | 'reset' | 'button'
// typeof btn
component?: 'button' | 'a'
// Set the loading state of the button
loading?: boolean
// Whether it is a ghost button
ghost?: true | false | 'light' | 'dark'
// Whether it is a text button
text?: boolean
// Whether it is a warning button
warning?: boolean
// Whether it is disabled
disabled?: boolean
// Callback for button click
onClick?: (e: {}) => void
// Valid when Button component is set to 'a', which represents the URL of the linked page
href?: string
// Valid when Button component is set to 'a', which represents the way of open the linked document
target?: string
}
<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)
}
deprecated,please use SchemaMarkupField
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'))
import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
Field,
FormItemGrid,
FormButtonGroup,
Submit,
Reset,
FormBlock,
FormLayout
} from '@uform/next'
import '@alifd/next/dist/next.css'
import Printer from '@uform/printer'
const App = () => {
const [value, setValues] = useState({})
useEffect(() => {
setTimeout(() => {
setValues({
array: [{ array2: [{ aa: '123', bb: '321' }] }]
})
}, 1000)
}, [])
return (
<Printer>
<SchemaForm initialValues={value} onSubmit={v => console.log(v)}>
<Field
title="Array"
name="array"
maxItems={3}
type="array"
x-props={{
renderAddition: 'Add Text',
renderRemove: 'Remove Text'
}}
>
<Field type="object">
<FormBlock title="Object Field">
<FormLayout labelCol={9} wrapperCol={6}>
<Field name="aa" type="string" title="field1" />
<Field name="bb" type="string" title="field2" />
<FormItemGrid title="field3" gutter={10}>
<Field name="cc" type="string" />
<Field name="dd" type="string" />
</FormItemGrid>
</FormLayout>
</FormBlock>
<FormBlock title="Nested Array Field">
<Field name="array2" maxItems={3} type="array">
<Field type="object">
<FormLayout labelCol={9} wrapperCol={6}>
<Field name="aa" type="string" title="field1" />
<Field name="bb" type="string" title="field2" />
<FormItemGrid title="field3" gutter={10}>
<Field name="cc" type="string" />
<Field name="dd" type="string" />
</FormItemGrid>
</FormLayout>
</Field>
</Field>
</FormBlock>
</Field>
</Field>
<FormButtonGroup>
<Submit>Submit</Submit>
<Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
Field,
FormItemGrid,
FormButtonGroup,
Submit,
Reset,
FormBlock,
FormLayout
} from '@uform/next'
import '@alifd/next/dist/next.css'
import Printer from '@uform/printer'
const App = () => (
<Printer>
<SchemaForm>
<Field
name="array"
maxItems={3}
type="array"
x-component="cards"
x-props={{
title: 'Title of cards',
renderAddition: 'Add Text',
renderRemove: 'Remove Text'
}}
>
<Field type="object">
<FormLayout labelCol={6} wrapperCol={8}>
<Field
name="aa"
type="string"
description="hello world"
title="field1"
/>
<Field name="bb" type="string" title="field2" />
<Field name="cc" type="string" title="field3" />
<Field name="dd" type="string" title="field4" />
<Field name="dd" type="string" title="field5" />
<Field name="ee" type="string" title="field6" />
<Field name="ff" type="string" title="field7" />
<Field name="gg" type="daterange" title="field8" />
</FormLayout>
<Field
name="array"
maxItems={3}
type="array"
x-component="cards"
x-props={{ title: 'Title of cards' }}
>
<Field type="object">
<FormLayout labelCol={6} wrapperCol={8}>
<Field
name="aa"
type="string"
description="hello world"
title="field1"
/>
<Field name="bb" type="string" title="field2" />
<Field name="cc" type="string" title="field3" />
<Field name="dd" type="string" title="field4" />
<Field name="dd" type="string" title="field5" />
<Field name="ee" type="string" title="field6" />
<Field name="ff" type="string" title="field7" />
<Field name="gg" type="daterange" title="field8" />
</FormLayout>
</Field>
</Field>
</Field>
</Field>
</SchemaForm>
</Printer>
)
ReactDOM.render(<App />, document.getElementById('root'))
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
Field,
FormItemGrid,
FormButtonGroup,
Submit,
Reset,
FormBlock,
FormLayout
} from '@uform/next'
import '@alifd/next/dist/next.css'
import Printer from '@uform/printer'
const App = () => (
<Printer>
<SchemaForm>
<FormLayout>
<Field
title="Array"
name="array"
maxItems={3}
type="array"
x-component="table"
x-props={{
renderExtraOperations() {
return <div>Hello worldasdasdasdasd</div>
},
operationsWidth: 300
}}
>
<Field type="object">
<Field
name="aa"
type="string"
description="hello world"
title="field1"
/>
<Field name="bb" type="string" title="field2" />
<Field name="cc" type="string" title="field3" />
<Field name="dd" type="string" title="field4" x-index={1} />
<Field name="ee" type="string" title="field5" />
<Field name="ff" type="string" title="field6" />
<Field name="gg" type="string" title="field7" />
<Field name="hh" type="daterange" title="field8" />
</Field>
</Field>
</FormLayout>
</SchemaForm>
</Printer>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormCard/>
, fully inherited from CardProps。 The only difference between FormCard FormBlock is a border on the style
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, { FormCard, SchemaMarkupField as Field } from '@uform/next'
import '@alifd/next/dist/next.css'
const App = () => (
<SchemaForm>
<FormCard title="block">
<Field type="string" name="username" title="username" />
</FormCard>
</SchemaForm>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormBlock/>
, fully inherited from CardProps
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, { FormBlock, SchemaMarkupField as Field } from '@uform/next'
import '@alifd/next/dist/next.css'
const App = () => (
<SchemaForm>
<FormBlock title="block">
<Field type="string" name="username" title="username" />
</FormBlock>
</SchemaForm>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormStep/>
interface IFormStep {
dataSource: StepItemProps[]
/** next step props**/
// current
current?: number
// direction of step
direction?: 'hoz' | 'ver'
// Content arrangement in horizontal layout
labelPlacement?: 'hoz' | 'ver'
// shape of step
shape?: 'circle' | 'arrow' | 'dot'
readOnly?: boolean
// Whether to activate animation
animation?: boolean
className?: string
// Custom StepItem render
itemRender?: (index: number, status: string) => React.ReactNode
}
Usage
import {
SchemaForm,
Field,
FormButtonGroup,
Submit,
FormEffectHooks,
createFormActions,
FormGridRow,
FormItemGrid,
FormGridCol,
FormPath,
FormLayout,
FormBlock,
FormCard,
FormTextBox,
FormStep
} from '@uform/next'
import { Button } from '@alifd/next'
import '@alifd/next/dist/next.css'
const { onFormInit$ } = FormEffectHooks
const actions = createFormActions()
let cache = {}
export default () => (
<SchemaForm
onSubmit={values => {
console.log('submit')
console.log(values)
}}
actions={actions}
labelCol={{ span: 8 }}
wrapperCol={{ span: 6 }}
validateFirst
effects={({ setFieldState, getFormGraph }) => {
onFormInit$().subscribe(() => {
setFieldState('col1', state => {
state.visible = false
})
})
}}
>
<FormStep
style={{ marginBottom: 20 }}
dataSource={[
{ title: 'Step1', name: 'step-1' },
{ title: 'Step2', name: 'step-2' },
{ title: 'Step3', name: 'step-3' }
]}
/>
<FormCard name="step-1" title="Step1">
<Field name="a1" required title="A1" type="string" />
</FormCard>
<FormCard name="step-2" title="Step2">
<Field name="a2" required title="A2" type="string" />
</FormCard>
<FormCard name="step-3" title="Step3">
<Field name="a3" required title="A3" type="string" />
</FormCard>
<FormButtonGroup>
<Submit>Submit</Submit>
<Button onClick={() => actions.dispatch(FormStep.ON_FORM_STEP_PREVIOUS)}>
Prev
</Button>
<Button onClick={() => actions.dispatch(FormStep.ON_FORM_STEP_NEXT)}>
Next
</Button>
<Button
onClick={() => {
cache = actions.getFormGraph()
}}
>
Save State
</Button>
<Button
onClick={() => {
actions.setFormGraph(cache)
}}
>
Rollback State
</Button>
</FormButtonGroup>
</SchemaForm>
)
Props of
<FormLayout/>
interface IFormItemTopProps {
inline?: boolean
className?: string
style?: React.CSSProperties
labelCol?: number | { span: number; offset?: number }
wrapperCol?: number | { span: number; offset?: number }
}
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import {
SchemaForm,
Field,
FormButtonGroup,
Submit,
Reset,
FormItemGrid,
FormCard,
FormBlock,
FormLayout
} from '@uform/next'
import { Button } from '@alifd/next'
import Printer from '@uform/printer'
import '@alifd/next/dist/next.css'
const App = () => (
<Printer>
<SchemaForm>
<FormLayout labelCol={8} wrapperCol={6}>
<Field name="aaa" type="string" title="field1" />
<Field name="bbb" type="number" title="field2" />
<Field name="ccc" type="date" title="field3" />
</FormLayout>
<FormButtonGroup offset={8}>
<Submit>Submit</Submit> <Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormItemGrid/>
interface IFormItemGridProps {
cols?: Array<number | { span: number; offset: number }>
gutter?: number
/** next Form.Item props**/
// prefix od FormItem
prefix?: string
// label od FormItem
label?: React.ReactNode
// label layout setting, eg, {span: 8, offset: 16}
labelCol?: {}
wrapperCol?: {}
// Custom prompt information, if not set, it will be automatically generated according to the verification rules.
help?: React.ReactNode
// Additional prompt information, similar to help, can be used when error messages and prompt copy are required at the same time. Behind the error message.
extra?: React.ReactNode
// Check status, if not set, it will be generated automatically according to check rules
validateState?: 'error' | 'success' | 'loading'
// Used in conjunction with the validateState property, whether to display the success / loading validation status icon. Currently only Input supports
hasFeedback?: boolean
// Custom inline style
style?: React.CSSProperties
// node or function(values)
children?: React.ReactNode | (() => void)
// The size of a single Item is customized, and takes precedence over the size of the Form, and when a component is used with an Item, the component itself does not set the size property.
size?: 'large' | 'small' | 'medium'
// Position of the label
labelAlign?: 'top' | 'left' | 'inset'
// alignment of labels
labelTextAlign?: 'left' | 'right'
className?: string
// [validation] required
required?: boolean
// whether required asterisks are displayed
asterisk?: boolean
// required custom error message
requiredMessage?: string
// required Custom trigger method
requiredTrigger?: string | Array<any>
// [validation] min
min?: number
// [validation] max
max?: number
// min/max error message
minmaxMessage?: string
// min/max custom trigger method
minmaxTrigger?: string | Array<any>
// [validation] min length of string / min length of array
minLength?: number
// [validation] max length of string / max length of array
maxLength?: number
// minLength/maxLength custom error message
minmaxLengthMessage?: string
// minLength/maxLength custom trigger method
minmaxLengthTrigger?: string | Array<any>
// [validation] length of string / length of array
length?: number
// length custom error message
lengthMessage?: string
// length custom trigger method
lengthTrigger?: string | Array<any>
// Regular pattern
pattern?: any
// pattern custom error message
patternMessage?: string
// pattern custom trigger method
patternTrigger?: string | Array<any>
// [validation] regular pattern
format?: 'number' | 'email' | 'url' | 'tel'
// format custom error message
formatMessage?: string
// format custom trigger method
formatTrigger?: string | Array<any>
// [validation] custom validator
validator?: () => void
// validator custom trigger method
validatorTrigger?: string | Array<any>
// Whether to automatically trigger validate when data is modified
autoValidate?: boolean
}
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import {
SchemaForm,
Field,
FormButtonGroup,
Submit,
Reset,
FormItemGrid,
FormCard,
FormBlock,
FormLayout
} from '@uform/next'
import { Button } from '@alifd/next'
import Printer from '@uform/printer'
import '@alifd/next/dist/next.css'
const App = () => (
<Printer>
<SchemaForm onSubmit={v => console.log(v)}>
<FormItemGrid gutter={20}>
<Field type="string" name="a1" title="field1" />
<Field type="string" name="a2" title="field2" />
<Field type="string" name="a3" title="field3" />
<Field type="string" name="a4" title="field4" />
</FormItemGrid>
<FormItemGrid gutter={20} cols={[6, 6]}>
<Field type="string" name="a5" title="field5" />
<Field type="string" name="a6" title="field6" />
</FormItemGrid>
<FormButtonGroup style={{ minWidth: 150 }}>
<Submit>Submit</Submit><Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormTextBox/>
interface IFormTextBox {
text?: string
gutter?: number
title?: React.ReactText
description?: React.ReactText
}
Usage
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
SchemaForm,
Field,
FormTextBox,
FormCard,
FormLayout
} from '@uform/next'
import { Button } from '@alifd/next'
import Printer from '@uform/printer'
import '@alifd/next/dist/next.css'
const App = () => {
return (
<Printer>
<SchemaForm labelCol={8} wrapperCol={6} onSubmit={v => console.log(v)}>
<FormCard title="FormTextBox">
<FormLayout labelCol={8} wrapperCol={16}>
<FormTextBox
title="text label"
text="prefix%suffix prefix2%suffix2 prefix3%suffix3"
gutter={8}
>
<Field
type="string"
default={10}
required
name="aa1"
x-props={{ style: { width: 80 } }}
description="desc1"
/>
<Field
type="number"
default={20}
required
name="aa2"
description="desc2"
/>
<Field
type="number"
default={30}
required
name="aa3"
description="desc3"
/>
</FormTextBox>
</FormLayout>
</FormCard>
</SchemaForm>
</Printer>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<FormButtonGroup/>
interface IFormButtonGroupProps {
sticky?: boolean
style?: React.CSSProperties
itemStyle?: React.CSSProperties
className?: string
align?: 'left' | 'right' | 'start' | 'end' | 'top' | 'bottom' | 'center'
triggerDistance?: number
zIndex?: number
span?: ColSpanType
offset?: ColSpanType
}
Usage
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
SchemaForm,
Field,
FormButtonGroup,
Submit,
Reset,
FormItemGrid,
FormCard,
FormBlock,
FormLayout
} from '@uform/next'
import { Button } from '@alifd/next'
import Printer from '@uform/printer'
import '@alifd/next/dist/next.css'
const App = () => {
const [state, setState] = useState({ editable: true })
return (
<Printer>
<SchemaForm onSubmit={v => console.log(v)}>
<div>normal</div>
<FormButtonGroup style={{ minWidth: 150 }}>
<Submit>Submit</Submit><Reset>Reset</Reset>
</FormButtonGroup>
<div>sticky</div>
<FormButtonGroup offset={8} sticky>
<Submit>Submit</Submit>
<Button
type="primary"
onClick={() => setState({ editable: !state.editable })}
>
{state.editable ? 'Preview' : 'Edit'}
</Button>
<Reset>Reset</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<TextButton/>
, fully inherited from ButtonProps
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, { TextButton } from '@uform/next'
import '@alifd/next/dist/next.css'
const App = () => (
<SchemaForm>
<TextButton>content</TextButton>
</SchemaForm>
)
ReactDOM.render(<App />, document.getElementById('root'))
Props of
<CircleButton/>
, fully inherited from ButtonProps
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, { CircleButton } from '@uform/next'
import '@alifd/next/dist/next.css'
const App = () => (
<SchemaForm>
<CircleButton>ok</CircleButton>
</SchemaForm>
)
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
string
- Schema UI Component: Fusion-Next
<Input/>
,<Input.Textarea/>
,<Select/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="string"
required
title="Text"
name="text"
x-component-props={{
placeholder: 'input'
}}
/>
<Field
type="string"
enum={['1', '2', '3', '4']}
required
title="Simple Select"
name="simpleSelect"
x-component-props={{
placeholder: 'select'
}}
/>
<Field
type="string"
enum={[
{ label: 'One', value: '1' },
{ label: 'Two', value: '2' },
{ label: 'Three', value: '3' },
{ label: 'Four', value: '4' }
]}
required
title="Object Select"
name="objSelect"
x-component-props={{
placeholder: 'select'
}}
/>
<Field
type="string"
title="TextArea"
name="textarea"
x-component="textarea"
x-component-props={{
placeholder: 'textarea'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
string
- Schema UI Component: Fusion-Next
<Input.Textarea/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="string"
title="TextArea"
name="textarea"
x-component="textarea"
x-component-props={{
placeholder: 'textarea'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
password
- Schema UI Component: Fusion-Next
<Input htmlType="password"/>
interface IPasswordProps {
checkStrength: boolean
/** next input props **/
// value
value?: string | number
// default value
defaultValue?: string | number
// callback triggered when value change
onChange?: (value: string, e: React.ChangeEvent<HTMLInputElement>) => void
// callback triggered when keyboard is press
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>, opts: {}) => void
// Disabled
disabled?: boolean
// Max length
maxLength?: number
// Whether to show the maximum length style
hasLimitHint?: boolean
// When maxLength is set, whether to truncate beyond the string
cutString?: boolean
// readOnly
readOnly?: boolean
// Automatically remove the leading and trailing blank characters when trigger onChange
trim?: boolean
// placeholder
placeholder?: string
// callback triggered when focus
onFocus?: () => void
// callback triggered when blur
onBlur?: () => void
// Custom string length calculation
getValueLength?: (value: string) => number
className?: string
style?: React.CSSProperties
htmlType?: string
// name of field
name?: string
// state of field
state?: 'error' | 'loading' | 'success'
// label
label?: React.ReactNode
// whether to show clear
hasClear?: boolean
// whether to show border
hasBorder?: boolean
// size of field
size?: 'small' | 'medium' | 'large'
// callback triggered when enter is press
onPressEnter?: () => void
// Watermark (Icon type, shared with hasClear)
hint?: string
// Append content before text
innerBefore?: React.ReactNode
// Append content after text
innerAfter?: React.ReactNode
// Append content before input
addonBefore?: React.ReactNode
// Append content after input
addonAfter?: React.ReactNode
// Append text before input
addonTextBefore?: React.ReactNode
// Append text after input
addonTextAfter?: React.ReactNode
// (Native supported by input)
autoComplete?: string
// (Native supported by input)
autoFocus?: boolean
}
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="string"
title="Password"
name="password"
x-component="password"
x-component-props={{
placeholder: 'password'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
number
- Schema UI Component: Fusion-Next
<NumberPicker/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field type="number" required title="Number" name="number" />
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
boolean
- Schema UI Component: Fusion-Next
<Switch/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="boolean"
required
title="Boolean"
name="boolean"
x-component-props={{
checkedChildren: 'on',
unCheckedChildren: 'off'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
date
- Schema UI Component: Fusion-Next
<DatePicker/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="date"
required
title="DatePicker"
name="datePicker"
x-component-props={{
format: 'YYYY-MM-DD HH:mm:ss'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
time
- Schema UI Component: Fusion-Next
<TimePicker/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="time"
required
title="TimePicker"
name="timePicker"
x-component-props={{
format: 'YYYY-MM-DD HH:mm:ss'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
range
- Schema UI Component: Fusion-Next
<Range/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="range"
required
title="Range"
name="range"
x-component-props={{
min: 0,
max: 1024,
marks: [0, 1024]
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
upload
- Schema UI Component: Fusion-Next
<Upload/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="upload"
required
title="Card Upload"
name="upload2"
x-component-props={{
listType: 'card'
}}
/>
<Field
type="upload"
required
title="Dragger Upload"
name="upload1"
x-component-props={{
listType: 'dragger'
}}
/>
<Field
type="upload"
required
title="Text Upload"
name="upload3"
x-component-props={{
listType: 'text'
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
checkbox
- Schema UI Component: Fusion-Next
<Checkbox/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="checkbox"
required
title="Simple Checkbox"
name="checkbox"
enum={['1', '2', '3', '4']}
/>
<Field
type="checkbox"
required
title="Object Checkbox"
name="checkbox2"
enum={[
{ label: 'One', value: '1' },
{ label: 'Two', value: '2' },
{ label: 'Three', value: '3' },
{ label: 'Four', value: '4' }
]}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
radio
- Schema UI Component: Fusion-Next
<Radio/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="radio"
required
title="Simple Radio"
name="radio"
enum={['1', '2', '3', '4']}
/>
<Field
type="radio"
required
title="Object Radio"
name="radio2"
enum={[
{ label: 'One', value: '1' },
{ label: 'Two', value: '2' },
{ label: 'Three', value: '3' },
{ label: 'Four', value: '4' }
]}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
rating
- Schema UI Component: Fusion-Next
<Rating/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="rating"
title="Rating"
name="rating"
x-component-props={{
allowHalf: true
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
- Schema Type :
transfer
- Schema UI Component: Fusion-Next
<Transfer/>
Usage
import React from 'react'
import ReactDOM from 'react-dom'
import SchemaForm, {
SchemaMarkupField as Field,
createFormActions,
FormBlock,
FormLayout,
FormButtonGroup,
Submit,
Reset
} from '@uform/next'
import '@alifd/next/dist/next.css'
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions}>
<Field
type="transfer"
title="Transfer"
name="transfer"
enum={[
{ label: 'One', value: '1' },
{ label: 'Two', value: '2' },
{ label: 'Three', value: '3' },
{ label: 'Four', value: '4' }
]}
x-component-props={{
showSearch: true
}}
/>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
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 SchemaForm, {
SchemaMarkupField as Field,
VirtualField,
createFormActions,
useFormEffects,
LifeCycleTypes,
createVirtualBox
} from '@uform/next'
const actions = createFormActions()
const FragmentContainer = createVirtualBox('ffb', (props) => {
useFormEffects(($, { setFieldState }) => {
$(LifeCycleTypes.ON_FORM_MOUNT).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>
{props.children}
</React.Fragment>
)
});
const FormFragment = () => {
return <FragmentContainer>
<Field
type="radio"
name="trigger"
title="trigger"
enum={[{ label: 'show', value: true }, { label: 'hide', value: false } ]}
/>
<Field type="string" name="a" title="a" />
<Field type="string" name="a-copy" title="a-copy" />
</FragmentContainer>
}
const App = () => {
return (
<SchemaForm actions={actions}>
<FormFragment />
</SchemaForm>
)
}
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>
}
Fully inherited from @uform/react, The specific API of @uform/next is listed below.
Return IFormActions
Signature
createFormActions(): IFormActions
Usage
import { createFormActions } from '@uform/next'
const actions = createFormActions()
console.log(actions.getFieldValue('username'))
Return IFormAsyncActions
Signature
createAsyncFormActions(): IFormAsyncActions
Usage
import { createAsyncFormActions } from '@uform/next'
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 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
Usage
import SchemaForm, { createEffectHook, createFormActions } from '@uform/next'
const actions = createFormActions()
const diyHook1$ = createEffectHook('diy1')
const diyHook2$ = createEffectHook('diy2')
const App = () => {
return (
<SchemaForm
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>
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Wrap field components with value / defaultValue / onChange of field, which make it esaily make custom field
type Connect = <T extends React.ComponentType<IFieldProps>>(options?: IConnectOptions<T>) =>
(Target: T) => React.PureComponent<IFieldProps>
Usage
import {registerFormField,connect} from '@uform/next'
registerFormField(
'string',
connect()(props => <input {...props} value={props.value || ''} />)
)
type registerFormField(
name : string, // name of field
component : React.ComponentType<IFieldProps>, // component of field
noMiddleware: boolean // whether use middleware
)
Usage
import SchemaForm, { SchemaMarkupField as Field, registerFormField, connect, createFormActions } from '@uform/next'
registerFormField(
'custom-string',
connect()(props => <input {...props} value={props.value || ''} />)
)
const actions = createFormActions()
const App = () => {
return (
<SchemaForm actions={actions} >
<Field type="custom-string" name="custom-string" title="Custom Field" />
</SchemaForm>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
The Interfaces is fully inherited from @uform/react. The specific Interfaces of @uform/next is listed below.
Schema protocol object, mainly used to constrain a json structure to satisfy the Schema protocol
interface ISchema {
/** base json schema spec**/
title?: React.ReactNode
description?: React.ReactNode
default?: any
readOnly?: boolean
writeOnly?: boolean
type?: 'string' | 'object' | 'array' | 'number' | string
enum?: Array<string | number | { label: React.ReactNode; value: any }>
const?: any
multipleOf?: number
maximum?: number
exclusiveMaximum?: number
minimum?: number
exclusiveMinimum?: number
maxLength?: number
minLength?: number
pattern?: string | RegExp
maxItems?: number
minItems?: number
uniqueItems?: boolean
maxProperties?: number
minProperties?: number
required?: string[] | boolean
format?: string
/** nested json schema spec **/
properties?: {
[key: string]: ISchema
}
items?: ISchema | ISchema[]
additionalItems?: ISchema
patternProperties?: {
[key: string]: ISchema
}
additionalProperties?: ISchema
/** extend json schema specs */
visible?: boolean //Field initial visible status(Whether the data is visible)
display?: boolean //Field initial display status(Whether the style is visible)
editable?: boolean
['x-props']?: { [name: string]: any }
['x-index']?: number
['x-rules']?: ValidatePatternRules
['x-component']?: string
['x-component-props']?: { [name: string]: any }
['x-render']?: <T = ISchemaFieldComponentProps>(
props: T & {
renderComponent: () => React.ReactElement
}
) => React.ReactElement
['x-effect']?: (
dispatch: (type: string, payload: any) => void,
option?: object
) => { [key: string]: 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
}>
/** get Schema of form **/
getFormSchema(): Schema
/*
* 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
/*
* 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>
/** get Schema of form **/
getFormSchema(): Promise<Schema>
/*
* 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) => 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 ButtonProps {
/** reset pops **/
onSubmit?: ISchemaFormProps['onSubmit']
showLoading?: boolean
/** nextBtnProps **/
// type of btn
type?: 'primary' | 'secondary' | 'normal'
// size of btn
size?: 'small' | 'medium' | 'large'
// size of Icon
iconSize?: 'xxs' | 'xs' | 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl'
// type of button when component = 'button'
htmlType?: 'submit' | 'reset' | 'button'
// typeof btn
component?: 'button' | 'a'
// Set the loading state of the button
loading?: boolean
// Whether it is a ghost button
ghost?: true | false | 'light' | 'dark'
// Whether it is a text button
text?: boolean
// Whether it is a warning button
warning?: boolean
// Whether it is disabled
disabled?: boolean
// Callback for button click
onClick?: (e: {}) => void
// Valid when Button component is set to 'a', which represents the URL of the linked page
href?: string
// Valid when Button component is set to 'a', which represents the way of open the linked document
target?: string
}
interface CardProps extends HTMLAttributesWeak, CommonProps {
// media in card
media?: React.ReactNode
// title of the card
title?: React.ReactNode
// subTitle of the card
subTitle?: React.ReactNode
// action button of the card
actions?: React.ReactNode
// whether to show bullet of title
showTitleBullet?: boolean
// whether to show divider of head
showHeadDivider?: boolean
contentHeight?: string | number
// extra content of card
extra?: React.ReactNode
// whether to set free mode, title, subtitle will be invalid when this options turns on
free?: boolean
}
interface ICompatItemProps
extends Exclude<ItemProps, 'labelCol' | 'wrapperCol'>,
Partial<ISchemaFieldComponentProps> {
labelCol?: number | { span: number; offset?: number }
wrapperCol?: number | { span: number; offset?: number }
}
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 ISchemaFieldComponentProps extends IFieldState {
schema: Schema
mutators: IMutators
form: IForm
renderField: (
addtionKey: string | number,
reactKey?: string | number
) => React.ReactElement
}
interface ISchemaVirtualFieldComponentProps extends IVirtualFieldState {
schema: Schema
form: IForm
children: React.ReactElement[]
renderField: (
addtionKey: string | number,
reactKey?: string | number
) => React.ReactElement
}
interface ISchemaFieldWrapper<Props = any> {
(Traget: ISchemaFieldComponent):
| React.FC<Props>
| React.ClassicComponent<Props>
}
declare type ISchemaFieldComponent = ComponentWithStyleComponent<
ISchemaFieldComponentProps
> & {
__WRAPPERS__?: ISchemaFieldWrapper[]
}
declare type ISchemaVirtualFieldComponent = ComponentWithStyleComponent<
ISchemaVirtualFieldComponentProps
> & {
__WRAPPERS__?: ISchemaFieldWrapper[]
}
interface ISchemaFormRegistry {
fields: {
[key: string]: ISchemaFieldComponent
}
virtualFields: {
[key: string]: ISchemaVirtualFieldComponent
}
wrappers?: ISchemaFieldWrapper[]
formItemComponent: React.JSXElementConstructor<any>
formComponent: string | React.JSXElementConstructor<any>
}
interface INextSchemaFieldProps {
name?: string;
/** ISchema **/
title?: SchemaMessage;
description?: SchemaMessage;
default?: any;
readOnly?: boolean;
writeOnly?: boolean;
type?: 'string' | 'object' | 'array' | 'number' | string;
enum?: Array<string | number | {
label: SchemaMessage;
value: any;
}>;
const?: any;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string | RegExp;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
required?: string[] | boolean;
format?: string;
properties?: {
[key: string]: ISchema;
};
items?: ISchema | ISchema[];
additionalItems?: ISchema;
patternProperties?: {
[key: string]: ISchema;
};
additionalProperties?: ISchema;
editable?: boolean;
visible?: boolean;
display?: boolean;
['x-props']?: {
[name: string]: any;
};
['x-index']?: number;
['x-rules']?: ValidatePatternRules;
['x-component']?: string;
['x-component-props']?: {
[name: string]: any;
};
['x-render']?: <T = ISchemaFieldComponentProps>(props: T & {
renderComponent: () => React.ReactElement;
}) => React.ReactElement;
['x-effect']?: (dispatch: (type: string, payload: any) => void, option?: object) => {
[key: string]: any;
};
interface IPreviewTextProps {
className?: React.ReactText
dataSource?: any[]
value?: any
addonBefore?: React.ReactNode
innerBefore?: React.ReactNode
addonTextBefore?: React.ReactNode
addonTextAfter?: React.ReactNode
innerAfter?: React.ReactNode
addonAfter?: React.ReactNode
}
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 IFieldProps<V = any> {
name : string // Node path
path : Array<string> // Data path
value : V // value
errors : Array<string> // Field error message
editable : boolean | ((name:string) => boolean) // Is it editable?
locale : Locale // i18n locale
loading : boolean // Is it in the loading state, note: if the field is in asynchronous verification, loading is true
schemaPath : Array<string> // schema path
getSchema : (path: string) => ISchema // get schema by path
renderField : (childKey: string, reactKey: string | number) => JSX.Element | string | null
renderComponent : React.FunctionComponent<Partial<IFieldProps> | undefined>,
getOrderProperties : () => Array<{schema: ISchema, key: number, path: string, name: string }>,
mutators : IMutators,
schema : ISchema
}
interface IConnectOptions<T> {
// name of value property
valueName?: string
// name of event property
eventName?: string
// default props
defaultProps?: Partial<IConnectProps>
// In some case, the value of our event function is not the first parameter of the event callback, and further customization is required.
getValueFromEvent?: (props: IFieldProps['x-props'], event: Event, ...args: any[]) => any
// props transformer
getProps?: (connectProps: IConnectProps, fieldProps: IFieldProps) => IConnectProps
// component transformer
getComponent?: (
target: T,
connectProps: IConnectProps,
fieldProps: IFieldProps
) => T
}
interface ISpyHook {
form: IForm
state: any
type: string
}