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

basic-react-form

v1.0.3

Published

React Form

Downloads

10

Readme

basic-react-form

npm version

Install

npm install basic-react-form --save

Use

import React, { Component } from 'react'
import { Form, Field, Submit } from 'basic-react-form'

export default class Example extends Component {
  validatePhone = (value) => {
    if (value && !isValidPhoneNumber(value)) {
      return 'Invalid phone number'
    }
  }

  // Can be `async/await`.
  // Can return a `Promise`.
  submit = (values) => {
    console.log(values)
    return new Promise(resolve => setTimeout(resolve, 3000))
  }

  render() {
    const { user } = this.props

    return (
      <Form onSubmit={ this.submit }>
        <Field
          required
          name="phone"
          component={ TextInput }
          type="tel"
          placeholder="Enter phone number"
          // Initial value for this field.
          value={ user.phone }
          validate={ this.validatePhone } />

        <Submit component={ SubmitButton }>
          Save
        </Submit>
      </Form>
    )
  }
}

// `error` is passed if the field is invalid.
function TextInput({ error, ...rest }) {
  return (
    <div>
      <input type="text" { ...rest }/>
      { error && <div className="error">{ error }</div> }
    </div>
  )
}

// `wait` is `true` while form is submitting.
function SubmitButton({ wait, children }) {
  return (
    <button type="submit" disabled={ wait || false }>
      { children }
    </button>
  )
}

API

Form

The <Form/> takes the following required properties:

  • onSubmit : Function(values) — Can be async or return a Promise.

The <Form/> takes the following optional properties:

  • trim : Boolean – Set to false to disable trimming strings. Defaults to true.

  • requiredMessage : String – The default error message for <Field required/>. Is "Required" by default.

  • onError : Function(Error) — Submit error handler. E.g. can show a popup with error message.

  • autoFocus : Boolean — Set to true to automatically focus on the first form field when the form is mounted. Defauls to false.

  • onBeforeSubmit : Function

  • onAfterSubmit : Function

  • onAbandon : Function(fieldName, fieldValue) — If a form field was focused but then the form wasn't submitted and was unmounted then this function is called meaning that the user possibly tried to fill out the form but then decided to move on for some reason (e.g. didn't know what to enter in a particular form field).

The <Form/> component instance (ref) provides the following methods:

  • focus(fieldName : String) — focuses on a field.

  • scroll(fieldName : String) — scrolls to a field (if it's not visible on the screen).

  • clear(fieldName : String) — clears field value.

  • get(fieldName : String) — gets form field value.

  • set(fieldName : String, value : String) — sets form field value.

  • values() — returns form field values (an alternative to get(fieldName : String)).

  • reset() — resets the form.

Upon form submission, if any one of its fields is invalid, then that field will be automatically scrolled to and focused, and the actual form submission won't happen.

Field

<Field/> takes the following required properties:

  • name : String

  • component : (React.Component|Function|String) — React component (can also be a string like input).

<Field/> takes the following optional properties:

  • value - the initial value of the field.

  • validate(value, allFormValues) : String — form field value validation function returning an error message if the field value is invalid.

  • error : String - an error message which can be set outside of the validate() function. Can be used for some hypothetical advanced use cases.

  • required : String or Boolean — adds "this field is required" validation for the <Field/> with the error message equal to required property value if it's a String defaulting to "Required" otherwise. Note that value={false} is valid in case of required because false is a non-empty value (e.g. "Yes"/"No" dropdown), therefore use validate function instead of required for checkboxes that are required to be checked, otherwise an unchecked checkbox will have value={false} and will pass the required check.

<Field/> passes the following properties to the field component:

  • value

  • onChange

  • onFocus

  • onBlur

  • disabled : Boolean — is true when form is submitting.

  • required : Boolean — is true when the <Field/> is required and the value is missing.

  • error : String — error message.

  • All other properties are passed through.

The error display algorythm is as follows:

  • Initially error for a field is not passed.

  • Whenever the user submits the form, errors are displayed for all invalid form fields.

  • Whenever the user edits a field's value, error becomes undefined for that field while the user is focused on the field.

  • Whenever the user focuses out of a field it is re-validated and error is passed if it's invalid.

  • Whenever a new error property is manually set on the <Field/> component that error is displayed.

Therefore, the error message is only shown when the user is not editing the field. For example, while the user is typing a phone number that phone number is invalid until the used inputs it fully, but it wouldn't make sense to show the "Invalid phone number" error to the user while he is in the process of inputting the phone number (it would just be an annoying distraction).

Submit

<Submit/> takes the following required properties:

  • component : (React.Component|Function|String) — React component (can also be a string like button).

<Submit/> passes the following properties to the component:

  • wait : Boolean — indicates if the form is currently being submitted.

  • All other properties are passed through.

function Example() {
  return (
    <Form onSubmit={ ... }>
      <Field name="text" component={ Input } />

      <Submit component={ SubmitButton }>
        Submit
      </Submit>
    </Form>
  )
}

function SubmitButton({ wait, children }) {
  return (
    <button
      type="submit"
      disabled={ wait }>
      { wait && <Spinner/> }
      { children }
    </button>
  )
}

Field errors

An error property can be set on a <Field/> if this field was the reason form submission failed on the server side.

This must not be a simple client-side validation error because for validation errors there already is validate property. Everything that can be validated up-front (i.e. before sending form data to the server) should be validated inside validate function. All other validity checks which can not be performed without submitting form data to the server are performed on the server side and if an error occurs then this error goes to the error property of the relevant <Field/>.

For example, consider a login form. Username and password <Field/>s both have validate properties set to the corresponding basic validation functions (e.g. checking that the values aren't empty). That's as much as can be validated before sending form data to the server. When the form data is sent to the server, server-side validation takes place: the server checks if the username exists and that the password matches. If the username doesn't exist then an error is returned from the HTTP POST request and the error property of the username <Field/> should be set to "User not found" error message. Otherwise, if the username does exist, but, say, the password doesn't match, then the error property of the password <Field/> should be set to "Wrong password" error message.

One thing to note about <Field/> errors is that they must be reset before form data is sent to the server: otherwise it would always say "Wrong password" even if the password is correct this time. Another case is when the error is set to the same value again (e.g. the entered password is wrong once again) which will not trigger showing that error because the error is shown only when its value changes: nextProps.error !== this.props.error. This is easily solved too by simply resetting errors before form data is sent to the server.

import { connect } from 'react-redux'
import { Form, Field, Submit } from 'basic-react-form'

@connect(state => ({ loginError: state.loginForm.error }))
class LoginForm extends Component {
  validateNotEmpty(value) {
    if (!value) {
      return 'Required'
    }
  }

  submit(values) {
    // Clears `state.loginForm.error`
    dispatch({ type: 'LOGIN_FORM_CLEAR_ERROR' })

    // Sends form data to the server
    return dispatch(sendHTTPLoginRequest(values))
  }

  render() {
    const { loginError } = this.props

    return (
      <Form onSubmit={this.submit}>
        <Field
          name="username"
          component={Input}
          validate={this.validateNotEmpty}
          error={loginError === 'User not found' ? loginError : undefined} />

        <Field
          name="password"
          component={Input}
          validate={this.validateNotEmpty}
          error={loginError === 'Wrong password' ? loginError : undefined} />

        <Submit component={SubmitButton}>
          Log In
        </Submit>
      </Form>
    )
  }
}

function Input({ error, ...rest }) {
  return (
    <div>
      <input {...rest}/>
      { error && <div className="error">{ error }</div> }
    </div>
  )
}

License

MIT