0.1.86 • Published 3 years ago

react-hooks-useform v0.1.86

Weekly downloads
167
License
ISC
Repository
github
Last release
3 years ago

react-hooks-useform

react-hooks-useform implements React hooks to enable an complete, lightweight form implementation for React. It works best with Material-UI components but can be used with native react inputs as well.

Installation

npm install --save react-hooks-useform

yarn add react-hooks-useform

Documentation

const [fieldProps, formProps] = useForm(formConfiguration)

formConfiguration

PropTypeRequiredDescription
fieldsArrayYesArray of objects describing fieldConfiguration (see fieldConfiguration)
submitFunctionYes(values)=>void Function calls when submit is triggered on form.
validateFunction or Function[]NoCalled each time the form validates
initialValuesMap<String, any>NoValues to populate form fields. Can be lazy loaded.
optionsFormOptionsNooptional configurations

fieldConfiguration

PropTypeRequiredDescription
nameStringYesName of the field. Will be used to pull data from initialValues.
typeFieldTypesNoType of field data (text,boolean,select,number)
helperTextStringNoHelper/Error text associated with the field
optionalBooleanNoSpecifies if the field is optional or required for field validation
requiredMessageStringNoMessage to display when required field validation fails (Defaults to 'Required')
labelStringNoLabel to display with the field
normalizeFunctionNoNormlalization function during onChange event to normalize data such as numbers/phones etc.
valueanyNoInitial value used by the field (default: "")
validate(value: T, fieldName: String, getValues: () => IValues) => StringNoValidation function for field level validation. Can be combined with Form level validation.
optionsArrayNoPass through options if type is select
valueFromChange(event: Object) => TNoHelper function used if onChange handler needs to provide a value other than event.target.value

fieldProps Object

PropTypeDescription
errorBooleanTrue if error exists on field, otherwise false
helperTextStringHelper or error text
labelStringLabel provided by fieldConfiguration, Formatted as ${label} (optional) if field configured as an optional field. This can be customized in FormOptions
valueanyControlled value of the field
onBlurFunctionEvent triggered upon input blur
onChangeFunctionEvent triggered upon input change

formProps Object

PropTypeDescription
FormReact.ComponentComponent used to wrap the form inputs
submitFunctionFunction to invoke when form should be submitted. Will trigger validation and if successful, trigger formConfiguration.submit
addField(field: fieldConfiguration) => voidCall to add a new field dynamically to the form
removeField(fieldName: String) => voidCall to remove a field dynamically form the form

fieldConfiguration

PropTypeRequiredDescription
optionalLabelFormatter(label: String) => StringNoFunction invoked to format labels for fields that are configured as optional

Examples

The following examples utilize Material-UI inputs

Simple Form

const SimpleForm = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { SimpleForm }

Optional Fields

const SimpleFormOptionalFields = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname', optional: true },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { SimpleFormOptionalFields }

Customize optional field prompt

const CustomizeOptionalPrompt = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname', optional: true },
    ],
    options: {
      optionalLabelFormatter: label => `${label} - not required`,
    },
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { CustomizeOptionalPrompt }

Field level validation

const FieldValidation = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname', optional: true },
      { name: 'phone', label: 'Phone', validate: validatePhone },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <TextField {...fields.phone} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { FieldValidation }

Field input normalize

const normalizePhone = input => {
  let normalizedPhone
  ...
  return normalizedPhone
}

const FieldNormalize = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname', optional: true },
      { name: 'phone', label: 'Phone', validate: validatePhone, normalize: normalizePhone },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <TextField {...fields.phone} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { FieldNormalize }

Custom helper text

const HelperText = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname', optional: true },
      { name: 'phone', label: 'Phone', helperText: 'Please enter a phone number' },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <TextField {...fields.phone} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { HelperText }

Custom required field message

const CustomRequiredMessage = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name', requiredMessage: 'Name is Required' },
      { name: 'nickname', label: 'Nickname', optional: true },
      { name: 'phone', label: 'Phone', requiredMessage: 'Phone is Required' },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <TextField {...fields.phone} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { CustomRequiredMessage }

Boolean field

const BooleanToggle = () => {
  const SwitchField = ({ label, error, helperText, ...rest }) => {
    return (
      <FormControlLabel
        control={(
          <Switch
            {...rest}
          />
        )}
        label={label}
      />
    )
  }

  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
      { name: 'isAdmin', label: 'Administrator', type: 'boolean' },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <SwitchField {...fields.isAdmin} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { BooleanToggle }

Number field

const NumberField = () => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
      { name: 'age', label: 'Age', type: 'number', normalize: normalizeNumber },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <TextField {...fields.age} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { NumberField }

Select field

const SelectField = () => {
  const SelectComponent = ({
    label,
    error,
    value,
    helperText,
    children,
    options,
    ...rest
  }) => {

    return (
      <FormControl>
        {label && <InputLabel error={error}>{label}</InputLabel>}
        <Select
          value={value}
          {...rest}
          error={error}
        >
          {options.map((option) => {
            return <MenuItem key={options.value} value={option.value}>{option.label}</MenuItem>
          })}
        </Select>
        <FormHelperText error={error}>{helperText}</FormHelperText>
      </FormControl>
    )
  }

  const genderOptions = [
    { value: 'male', label: 'Male' },
    { value: 'female', label: 'Female' },
  ]

  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
      { name: 'gender', label: 'Gender', type: 'select', options: genderOptions },
    ],
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
        <SelectComponent {...fields.gender} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { SelectField }

Initial values passed into form

const InitialValuesSet = ({ initialValues }) => {
  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
    ],
    initialValues,
  })

  return (
    <form.Form>
      <Flexbox flexDirection='column'>
        <TextField {...fields.fullName} />
        <TextField {...fields.nickname} />
      </Flexbox>
      <Button type='submit' onClick={form.submit}>Submit</Button>
    </form.Form>
  )
}

export { InitialValuesSet }

Dynamic fields

const DynamicFields = () => {

  const [fields, form] = useForm({
    fields: [
      { name: 'fullName', label: 'Full Name' },
      { name: 'nickname', label: 'Nickname' },
    ],
  })

  const [dynamicFields, dynamicForm] = useForm({
    fields: [
      { name: 'name', label: 'Name' },
      { name: 'label', label: 'Label' },
    ],
    submit: values => {
      const name = values.get('name')
      const label = values.get('label')
      form.addField({ name, label })
    },
  })

  return (
    <Flexbox flexDirection='column'>
      <dynamicForm.Form>
        <Flexbox>
          <TextField {...dynamicFields.name} />
          <TextField {...dynamicFields.label} />
          <Button type='submit' onClick={dynamicForm.submit}>Add Field</Button>
        </Flexbox>
      </dynamicForm.Form>
      <Divider style={{ margin: '24px 16px 8px 16px' }} />
      <form.Form>
        <Flexbox flexDirection='column'>
          <TextField {...fields.fullName} />
          <TextField {...fields.nickname} />
          {Object.keys(fields)
            .filter(key => (key !== 'fullName' && key !== 'nickname'))
            .map(key => (
              <Flexbox key={key}>
                <TextField {...fields[key]} />
                <Button onClick={() => form.removeField(key)}>X</Button>
              </Flexbox>
            ))
          }
        </Flexbox>
        <Button type='submit' onClick={form.submit}>Submit</Button>
      </form.Form>
    </Flexbox>
  )
}

export { DynamicFields }
0.1.85

3 years ago

0.1.86

3 years ago

0.1.84

4 years ago

0.1.83

4 years ago

0.1.81

4 years ago

0.1.82

4 years ago

0.1.80

4 years ago

0.1.79

4 years ago

0.1.78

4 years ago

0.1.76

4 years ago

0.1.77

4 years ago

0.1.75

4 years ago

0.1.74

4 years ago

0.1.73

4 years ago

0.1.72

4 years ago

0.1.71

4 years ago

0.1.70

4 years ago

0.1.66

4 years ago

0.1.65

4 years ago

0.1.64

5 years ago

0.1.63

5 years ago

0.1.62

5 years ago

0.1.61

5 years ago

0.1.60

5 years ago

0.1.59

5 years ago

0.1.58

5 years ago

0.1.57

5 years ago

0.1.56

5 years ago

0.1.55

5 years ago

0.1.54

5 years ago

0.1.53

5 years ago

0.1.52

5 years ago

0.1.51

5 years ago

0.1.50

5 years ago

0.1.49

5 years ago

0.1.48

5 years ago

0.1.47

5 years ago

0.1.46

5 years ago

0.1.45

5 years ago

0.1.44

5 years ago

0.1.43

5 years ago

0.1.42

5 years ago

0.1.41

5 years ago

0.1.40

5 years ago

0.1.39

5 years ago

0.1.38

5 years ago

0.1.37

5 years ago

0.1.36

5 years ago

0.1.35

5 years ago

0.1.34

5 years ago

0.1.33

5 years ago

0.1.32

5 years ago

0.1.31

5 years ago

0.1.30

5 years ago

0.1.29

5 years ago

0.1.28

5 years ago

0.1.27

5 years ago

0.1.26

5 years ago

0.1.25

5 years ago

0.1.24

5 years ago

0.1.23

5 years ago

0.1.22

5 years ago

0.1.21

5 years ago

0.1.20

5 years ago

0.1.19

5 years ago

0.1.18

5 years ago

0.1.17

5 years ago

0.1.16

5 years ago

0.1.15

5 years ago

0.1.14

5 years ago

0.1.13

5 years ago

0.1.12

5 years ago

0.1.11

5 years ago

0.1.10

5 years ago

0.1.9

5 years ago

0.1.8

5 years ago

0.1.7

5 years ago

0.1.6

5 years ago

0.1.5

5 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago