npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ifcloud-formily/react

v1.5.6

Published

English | [简体中文](./README.zh-cn.md)

Downloads

4

Readme

@ifcloud-formily/react

English | 简体中文

@ifcloud-formily/react is based on react and @ifcloud-formily/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)

Install

npm install --save @ifcloud-formily/react

Table Of Contents

Usage


Quick Start

import React from 'react'
import ReactDOM from 'react-dom'
import {
  Form,
  Field,
  FormPath,
  createFormActions,
  FormSpy,
  FormProvider,
  FormConsumer,
  FormEffectHooks
} from '@ifcloud-formily/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'))

Basic Field

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>
)

Validation

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 '@ifcloud-formily/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'))

Object Field

Example:User info user(username, age)

import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@ifcloud-formily/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'))

ArrayField

Example:Id list

import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@ifcloud-formily/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'))

ArrayField<Object>

Example:User list

import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions } from '@ifcloud-formily/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'))

display visible

Example: see how displayvisible affect values

import React from 'react'
import ReactDOM from 'react-dom'
import {
  Form,
  Field,
  createFormActions,
  LifeCycleTypes,
  FormSpy
} from '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const CheckedField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              type="checkbox"
              onChange={() => {
                mutators.change(!state.value)
              }}
              checked={!!state.value}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={($, { validate, setFieldState }) => {
        $(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
          setFieldState('displayTrigger', state => (state.value = true))
          setFieldState('visibleTrigger', state => (state.value = true))
          setFieldState('a', state => (state.value = 1))
          setFieldState('b', state => (state.value = 2))
        })

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'visibleTrigger').subscribe(
          fieldState => {
            setFieldState('a', state => {
              state.visible = fieldState.value
            })
          }
        )

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'displayTrigger').subscribe(
          fieldState => {
            setFieldState('b', state => {
              state.display = fieldState.value
            })
          }
        )
      }}
    >
      <div>
        <CheckedField label="visible" name="visibleTrigger" />

        <InputField name="a" label="a" />
      </div>
      <div>
        <CheckedField label="display" name="displayTrigger" />
        <InputField name="b" label="b" />
      </div>

      <FormSpy>
        {({ state, form }) => {
          return (
            <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'))

Linkage

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 '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const CheckedField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              type="checkbox"
              onChange={() => {
                mutators.change(!state.value)
              }}
              checked={!!state.value}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={($, { setFieldState }) => {
        $(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
          setFieldState('a~', state => (state.visible = false))
        })

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
          triggerState => {
            setFieldState('a~', state => {
              state.visible = triggerState.value
            })
          }
        )

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
          setFieldState('a-copy', state => {
            state.value = fieldState.value
          })
        })
      }}
    >
      <CheckedField name="trigger" label="show/hide" />
      <div>
        <InputField label="a" name="a" />
      </div>
      <div>
        <InputField label="a-copy" name="a-copy" />
      </div>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Async Linkage

Example:Change dataSource in select asynchronously by effects

import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, LifeCycleTypes } from '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)
const CheckedField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              type="checkbox"
              onChange={() => {
                mutators.change(!state.value)
              }}
              checked={!!state.value}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const SelectField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const { loading, dataSource = [] } = state.props
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <select
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            >
              {dataSource.map(item => (
                <option value={item.value}>{item.label}</option>
              ))}
            </select>
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={($, { setFieldState }) => {
        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
          fieldState => {
            const dataSource = [
              { label: 'aa', value: 'aa' },
              { label: 'bb', value: 'bb' }
            ]
            setFieldState('sync-source', state => {
              state.props.dataSource = fieldState.value ? dataSource : []
            })
            setFieldState('async-source', state => {
              state.props.loading = true
            })

            setTimeout(() => {
              setFieldState('async-source', state => {
                state.props.loading = false
                state.props.dataSource = fieldState.value ? dataSource : []
              })
            }, 300)
          }
        )
      }}
    >
      <CheckedField name="trigger" label="show/reset dataSource" />
      <div>
        <SelectField label="sync-source" name="sync-source" />
      </div>
      <div>
        <SelectField label="async-source" name="async-source" />
      </div>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Linkage Validation

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 '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={($, { validate, setFieldState }) => {
        $(LifeCycleTypes.ON_FORM_MOUNT).subscribe(() => {
          validate()
        })

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
          setFieldState('a-copy', state => {
            state.value = fieldState.value
          })
        })
      }}
    >
      <InputField label="a" name="a" />
      <div>
        <InputField label="a-copy" name="a-copy" required />
      </div>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Complex Linkage

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 '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={($, { validate, setFieldState }) => {
        $(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
          setFieldState('userList.*.username', state => {
            state.visible = false
          })
        })

        $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
          fieldState => {
            setFieldState('userList.*.username', state => {
              state.visible = fieldState.value
            })
          }
        )
      }}
    >
      <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'))

Reuse Effects

Make your own reusable effects.

import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field, createFormActions, FormEffectHooks } from '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)
const CheckedField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              type="checkbox"
              onChange={() => {
                mutators.change(!state.value)
              }}
              checked={!!state.value}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const { onFormMount$, onFieldValueChange$ } = FormEffectHooks
const getEffects = () => {
  const actions = createFormActions()
  onFormMount$().subscribe(() => {
    actions.setFieldState('a~', state => (state.visible = false))
  })

  onFieldValueChange$('trigger').subscribe(triggerState => {
    actions.setFieldState('a~', state => {
      state.visible = triggerState.value
    })
  })

  onFieldValueChange$('a').subscribe(fieldState => {
    actions.setFieldState('a-copy', state => {
      state.value = fieldState.value
    })
  })
}

const actions = createFormActions()
const App = () => {
  return (
    <Form
      actions={actions}
      effects={() => {
        getEffects()
      }}
    >
      <CheckedField name="trigger" label="show/hide" />
      <div>
        <InputField label="a" name="a" />
      </div>
      <div>
        <InputField label="a-copy" name="a-copy" />
      </div>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Combo

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 '@ifcloud-formily/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'))

Provide and FormSpy

Dictionary
--app
  |---components
  |---customForm

Example:Cross-file consumption form state, Check FormProvider and FormSpy for more information.

import React from 'react'
import ReactDOM from 'react-dom'
import {
  Form,
  Field,
  createFormActions,
  FormSpy,
  FormProvider
} from '@ifcloud-formily/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'))

Deconstruction

import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
  Form,
  Field,
  createFormActions,
  FormSpy,
  FormPath
} from '@ifcloud-formily/react'

const actions = createFormActions()

const App = () => {
  return (
    <Form actions={actions}>
      <label>range input</label>
      <Field name="[start,end]">
        {({ state, mutators }) => {
          const [start, end] = state.value
          return (
            <div>
              <label>start</label>
              <input
                value={start}
                onChange={e => {
                  mutators.change([e.target.value, end])
                }}
              />
              <label>end</label>
              <input
                value={end}
                onChange={e => {
                  mutators.change([start, e.target.value])
                }}
              />
            </div>
          )
        }}
      </Field>
      <button
        onClick={() => {
          actions.setFormState(state => {
            state.values = { start: 'x', end: 'y' }
          })
        }}
      >
        set value
      </button>
      <FormSpy>
        {({ state, form }) => {
          return (
            <div>
              Form values:
              <code>
                <pre>
                  {JSON.stringify(
                    form.getFormState(state => state.values),
                    null,
                    2
                  )}
                </pre>
              </code>
            </div>
          )
        }}
      </FormSpy>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Complex Deconstruction

import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {
  Form,
  Field,
  createFormActions,
  FormSpy,
  FormPath
} from '@ifcloud-formily/react'

const actions = createFormActions()
const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const App = () => {
  return (
    <Form actions={actions}>
      <Field name="{aa:{bb:{cc:destructor1,dd:[destructor2,destructor3],ee}}}">
        {({ state, mutators }) => {
          return (
            <div>
              <button
                onClick={() => {
                  mutators.change({
                    aa: {
                      bb: {
                        cc: 123,
                        dd: [333, 444],
                        ee: 'abcde'
                      }
                    }
                  })
                }}
              >
                set value
              </button>
              <div>Field value:</div>
              <code>
                <pre>{JSON.stringify(state.value, null, 2)}</pre>
              </code>
            </div>
          )
        }}
      </Field>
      <button
        onClick={() => {
          actions.setFieldState(
            FormPath.match(
              '[[{aa:{bb:{cc:destructor1,dd:\\[destructor2,destructor3\\],ee}}}]]'
            ),
            state => {
              state.value = {
                aa: {
                  bb: {
                    cc: 'a',
                    dd: ['b', 'c'],
                    ee: 'd'
                  }
                }
              }
            }
          )
        }}
      >
        outside set
      </button>
      <FormSpy>
        {({ state, form }) => {
          return (
            <div>
              Form values:
              <code>
                <pre>
                  {JSON.stringify(
                    form.getFormState(state => state.values),
                    null,
                    2
                  )}
                </pre>
              </code>
            </div>
          )
        }}
      </FormSpy>
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Components


<Form/>

<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/>

<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 '@ifcloud-formily/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/>

<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 '@ifcloud-formily/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/>

<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 '@ifcloud-formily/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 '@ifcloud-formily/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'))

<FormProvider/>

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 '@ifcloud-formily/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/>(deprecated,pls using <FormSpy/>)

<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)
}

Hook

useFormEffects

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 '@ifcloud-formily/react'

const actions = createFormActions()
const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const CheckedField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              type="checkbox"
              onChange={() => {
                mutators.change(!state.value)
              }}
              checked={!!state.value}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const FormFragment = () => {
  useFormEffects(($, { setFieldState }) => {
    $(LifeCycleTypes.ON_FORM_INIT).subscribe(() => {
      setFieldState('a~', state => (state.visible = false))
    })

    $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'trigger').subscribe(
      triggerState => {
        setFieldState('a~', state => {
          state.visible = triggerState.value
        })
      }
    )

    $(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, 'a').subscribe(fieldState => {
      setFieldState('a-copy', state => {
        state.value = fieldState.value
      })
    })
  })

  return (
    <React.Fragment>
      <CheckedField name="trigger" label="show/hide" />
      <div>
        <InputField label="a" name="a" />
      </div>
      <div>
        <InputField label="a-copy" name="a-copy" />
      </div>
    </React.Fragment>
  )
}

const App = () => {
  return (
    <Form actions={actions}>
      <FormFragment />
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

useFormState

使用 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 '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const actions = createFormActions()
const FormFragment = props => {
  const [formState, setFormState] = useFormState({ extendVar: 0 })
  const { extendVar } = formState

  return (
    <div>
      <button
        onClick={() => {
          setFormState({ extendVar: extendVar + 1 })
        }}
      >
        add
      </button>
      <div>count: {extendVar}</div>
    </div>
  )
}

const App = () => {
  return (
    <Form actions={actions}>
      <FormFragment />
    </Form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

useFieldState

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 '@ifcloud-formily/react'

const InputField = props => (
  <Field {...props}>
    {({ state, mutators }) => {
      const loading = state.props.loading
      return (
        <React.Fragment>
          {props.label && <label>{props.label}</label>}
          {loading ? (
            ' loading... '
          ) : (
            <input
              disabled={!state.editable}
              value={state.value || ''}
              onChange={mutators.change}
              onBlur={mutators.blur}
              onFocus={mutators.focus}
            />
          )}
          <span style={{ color: 'red' }}>{state.errors}</span>
          <span style={{ color: 'orange' }}>{state.warnings}</span>
        </React.Fragment>
      )
    }}
  </Field>
)

const changeTab$ = createEffectHook('changeTab')
const actions = createFormActions()
const TabFragment = props => {
  const [fieldState, setLocalFieldState] = useFieldState({ current: 0 })
  const { current } = fieldState
  const { children, dataSource, form } = props
  const 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'))

useForm

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 '@ifcloud-formily/react'

const FormFragment = () => {
  const form = useForm()
  return <div>{form.getFieldValue('username')}</div>
}

useField

get IFieldHook instance

Signature

type useField = (options: IFieldStateUIProps): IFieldHook

Usage

import { useField } from '@ifcloud-formily/react'

const FormFragment = props => {
  const { form, state, props: fieldProps, mutators } = useField({
    name: 'username'
  })

  return (
    <input
      {...fieldProps}
      {...props}
      value={state.value}
      onChange={mutators.change}
    />
  )
}

useVirtualField

get IVirtualFieldHook instance

Signature

type UseVirtualField = (options: IVirtualFieldStateProps): IVirtualFieldHook

Usage

import { UseVirtualField } from '@ifcloud-formily/react'

const FormFragment