0.1.0 • Published 4 years ago

simple-form-react v0.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

Simple Form React 📝

Custom React Hook for handling small & medium forms

Features

  • Auto escape & trim inputs
  • Auto check if any field (when required: true) or only required field (HTML elements with required attribute; when required: false) is empty
  • Auto check if field, which name starts with confirm, matches its pair (e.g. password === confirmPassword)
  • Accepts custom validators and error messages objects

Installation

yarn add simple-form-react
# or
npm i simple-form-react

Props

  • initialData: { field: initial value }
  • url: string - endpoint for data
  • method: string - Post by default
  • required
    • if true (default), all fields will be required
    • if false, only inputs with attribute required will be required
    • value of disabled indicator depends on that
  • validators: { (field) => validation logic }
  • messages: { field: error message }
  • onChange: function
  • onSubmit: function
  • fetchOptions: object - options for very-simple-fetch

Returns

  • fields: { field: value }
  • change: function
  • submit: function
  • reset: function
  • disabled: boolean
  • loading: boolean
  • response: any, including server errors
  • errors: object - client (aka validation) errors

Usage

Full Example on CodeSandbox

Form.js

// import { useState } from 'react'
import { Loader } from 'components'

import { Field } from './Field'
import { Button } from './Button'
import { Result } from './Result'
import { Error } from './Error'

import { defaultValidators, errorMessagesClient } from './validators'

import useSimpleForm, { simpleFetch } from 'simple-form-react'
simpleFetch.baseUrl = 'http://localhost:5000/api/auth'

export const Form = (props) => {
  // const [data, setData] = useState(null)

  const { children, inputs, validators, messages, submitLabel, ...rest } = props

  const hookProps = {
    ...rest,
    validators: { ...defaultValidators, ...validators },
    messages: { ...errorMessagesClient, ...messages }
  }

  /*
  hookProps.fetchOptions = {
      handlers: {
        onSuccess: (response) => {
          setData(response.data)
        }
      }
    }
  */

  /*
  hookProps.onSubmit = async (fields) => {
    const response = await simpleFetch.post(rest.url, fields)
    setData(response.data)
  }
  */

  const { fields, change, submit, reset, disabled, loading, response, errors } =
    useSimpleForm(hookProps)

  if (loading) return <Loader />
  if (response?.data) return <Result result={response.data} />
  // if (data) return <Result result={data} />

  return (
    <form onSubmit={submit} onReset={reset}>
      {props.inputs.map((input, index) => (
        <Field
          key={index}
          value={fields[input.name]}
          onChange={change}
          error={errors[input.name]}
          {...input}
        />
      ))}
      {children}
      {response?.error && <Error status={response.info.status} />}
      <div>
        <Button label={submitLabel} variant='success' disabled={disabled} />
        <Button label='Reset' type='reset' variant='warning' />
      </div>
    </form>
  )
}

Field.js

const icons = ((i) => ({
  name: `${i}-person`,
  age: `${i}-person-fill`,
  email: `${i}-envelope`,
  password: `${i}-key`,
  confirmPassword: `${i}-key-fill`
}))('bi bi')

export const Field = ({ label, name, error, ...rest }) => (
  <div className='field'>
    <label htmlFor={name} className='form-label'>
      <i className={icons[name]}></i>
      <span>{label}</span>
    </label>
    <input name={name} id={name} className='form-control' {...rest} />
    <p className='text-danger'>{error}</p>
  </div>
)

Button.js

const buttons = ((b) => ({
  success: `${b}-success`,
  warning: `${b}-warning`
}))('btn btn')

export const Button = ({ label, variant, ...rest }) => (
  <button className={buttons[variant] || 'btn'} {...rest}>
    {label}
  </button>
)

Result.js

export const Result = ({ result }) => (
  <>
    <h4>Result</h4>
    <div>
      {Object.entries(result).map(([key, value], index) => (
        <p key={index}>
          {key}: {value}
        </p>
      ))}
    </div>
  </>
)

Error.js

import { errorMessagesServer } from './validators'

export const Error = ({ status }) => (
  <div className='text-danger'>
    <p>{errorMessagesServer[status]}</p>
  </div>
)

validators.js

const isEmail = (email) => /\S+@\S+\.\S+/.test(email)

export const defaultValidators = {
  email: isEmail
}

export const errorMessagesClient = {
  email: 'Wrong email!',
  confirmPassword: 'Passwords must be the same!'
}

export const errorMessagesServer = {
  404: 'User not found!',
  403: 'Wrong credentials!',
  409: 'Email already in use!',
  500: 'Something went wrong. Try again later'
}

Login.js

import { Form } from 'components'

const inputs = [
  {
    label: 'Email',
    name: 'email',
    type: 'email'
  },
  {
    label: 'Password',
    name: 'password',
    type: 'password'
  }
]

const initialData = {
  email: '',
  password: ''
}

const formProps = {
  initialData,
  url: '/login',
  inputs,
  submitLabel: 'Login'
}

export const Login = () => (
  <>
    <h3>Login</h3>
    <Form {...formProps} />
  </>
)

Register.js

import { Form } from 'components'

const inputs = [
  {
    label: 'Name',
    name: 'name',
    type: 'text',
    minlength: 2
  },
  {
    label: 'Age',
    name: 'age',
    type: 'number',
    min: 18,
    max: 65,
    step: 1
  },
  {
    label: 'Email',
    name: 'email',
    type: 'email',
    required: true
  },
  {
    label: 'Password',
    name: 'password',
    type: 'password',
    required: true
  },
  {
    label: 'Confirm password',
    name: 'confirmPassword',
    type: 'password',
    required: true
  }
]

const validators = {
  name: (value) => value.length > 1,
  age: (value) => value > 17 && value < 66
}

const messages = {
  name: 'Your name is too short!',
  age: `You're too young or too old!`
}

const initialData = {
  name: '',
  age: '',
  email: '',
  password: '',
  confirmPassword: ''
}

const formProps = {
  initialData,
  url: '/register',
  inputs,
  required: false,
  validators,
  messages,
  submitLabel: 'Register'
}

export const Register = () => (
  <>
    <h3>Register</h3>
    <Form {...formProps} />
  </>
)
0.1.0

4 years ago

0.0.9

4 years ago

0.0.8

4 years ago

0.0.5

4 years ago

0.0.7

4 years ago

0.0.6

4 years ago

0.0.4

4 years ago

0.0.3

4 years ago

0.0.2

4 years ago

0.0.1

4 years ago