Skip to content

Basic Usage Guide

Michal Katanski edited this page Oct 18, 2017 · 4 revisions

Creating simple form

Create a "Form" component

// components/Form.js
import React from 'react'
import { StrapForm, StrapFormPropTypes } from 'strap-forms'

const FormPure = ({ children, handleSubmit }) => (
  <form onSubmit={handleSubmit} >{children}</form>
)

FormPure.propTypes = {
  ...StrapFormPropTypes,
}

export default StrapForm(FormPure)

For the purpose of this guide, the example above is the simplest possible. In fact, the only thing you should do is pass the handleSubmit property to the onSubmit property of the standard form component.

The StrapForm HoC exposes several different properties for the component. To make life easier, you can use a ready-made object (StrapFormPropTypes) for types instead of writing them individually.

Create an "Input" component

Let's create an input that will display all validation errors and warnings in a basic way. Our component will also inform the user that it is in the process of performing asynchronous validation.

// components/Input.js

import { StrapInput, StrapInputPropTypes } from 'strap-forms'

class InputPure extends Component {
  static propTypes = {
    ...StrapInputPropTypes,
  }

  get errors() {
    const { errors, warnings, touched } = this.props

    return (
      <div>
        <ul className="errors">
          {touched && Object.keys(errors).map(k => <li key={k}>{errors[k]}</li>)}
        </ul>
        <ul className="warnings">
          {touched && Object.keys(warnings).map(k => <li key={k}>{warnings[k]}</li>)}
        </ul>
      </div>
    )
  }

  render() {
    const { input, isValidating } = this.props

    return (
      <div>
        <input {...input} />
        {isValidating && <span>validating...</span>}
        {this.errors}
      </div>
    )
  }
}

export default StrapInput(InputPure)

Displaying errors and warnings is relatively straightforward. However, in the future you may want to create a separate component that will display this information regardless of the input component. Strap Forms makes it easy to do so, but it goes beyond this guide.

Put all together

// MyForm.js

import React from 'react'
import validate from 'validate'

import Form from './components/Form.js'
import Input from './components/Input.js'

const apiSimulate = ms => new Promise(resolve => setTimeout(resolve, ms))

class MyForm extends React.Component {

  handleSubmit = ({ values }) => {
    console.log('The form is submitting', values)
  }

  render() {
    const isEmail = val => (/\S+@\S+\.\S+/.test(val) ? undefined : 'Invalid email')
    const gmail = val => (val.indexOf('gmail.com') === -1 ? undefined : 'Why Gmail?')
    const asyncEmail = val => apiSimulate(5000).then(() => {
      if (value === '[email protected]') {
        throw new Error('This address is already taken')
      }
    })

    return (
      <Form onSubmit={this.handleSubmit}>
        <p>Type [email protected] to see async validation error</p>
        <div>
          <Input
            name="email"
            validate={[isEmail]}
            warn={[gmail]}
            asyncValidation={asyncEmail}
          />
        </div>

        <p>Same as above but without warning</p>
        <div>
          <Input
            name="secondEmail"
            validate={[isEmail]}
            asyncValidation={asyncEmail}
          />
        </div>

        <button type="submit">Submit</button>
      </Form>
    )
  }
}

Note that submit button, in this case, will not respond to changes in the form, eg: invalid state. For example, to implement disabling button when the form has errors, you can use <MyForm /> state but it will lead to rerendering all components each time the change is made. Strap Forms offer better solution by using context events.

Let's look at how to write your own button:

// components/SubmitButton.js

import React from 'react'
import T from 'prop-types'

import { StrapFormContextTypes } from 'strap-form'

class SubmitButton extends React.Component {
  static contextTypes = {
    ...StrapFormContextTypes,
  }

  static propTypes = {
    children: T.any.isRequired,
  }

  state = {
    disabled: false,
  }

  componentDidMount() {
    this.context.listenTo('onFormUpdate', ({ isValid, isSubmitting, isPristine }) => {
      if (isPristine) return
      this.setState({ disabled: !(isValid && !isSubmitting) })
    })
  }

  render() {
    return (
      <button
        type="submit"
        disabled={this.state.disabled}
      >
        {this.props.children}
      </button>
    )
  }
}

export default SubmitButton

and in MyForm.js, instead of <button type="submit">Submit</button>, write:

...
<SubmitButton>Submit</SubmitButton>
...

Let's briefly analyze the button component. Unlike previous components, this does not use the HoC pattern. There is no need, because all we need is to listen for a specific event. To do this, we add a listener using the addListener method. It is passed in through the context to each component that is a child of StrapForm. The onFormUpdate event is invoked every time any input changes its state.

Clone this wiki locally