Reusable form in React
From Logic Wiki
Contents
Creating Form
in common folder we create a new file named form.jsx
import React, { Component } from 'react';
import Joi from 'joi-browser';
import Input from './input';
class Form extends Component {
state ={
data:{},
errors:{}
};
validate = () => {
const options = { abortEarly:false};
const { error } = Joi.validate(this.state.account, this.schema, options);
if(!error) return null
const errors = {};
for (let item of result.error.details)
{
errors[item.path[0]] = item.message;
}
return errors;
};
validateProperty = ({name, value}) => {
const obj = { [name]: value };
const schema = { [name]: this.schema[name] };
const {error} = Joi.validate(obj, schema)
return error ? error.details[0].message : null;
};
handleSubmit = e => {
e.preventDefault();
const errors = this.validate();
this.setState({errors: errors || {} });
if (errors) return;
this.doSubmit();
}
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = {...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
}
renderButton(label) {
return (
<button disabled={this.valdiate()} className="btn btn-primary">
{label}
</button>
);
}
renderInput(name, label, type= 'text') {
const { data, errors } = this.state;
return(
<Input
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleChange}
error={errors[name]}
/>
);
}
renderSelect(name, label, options){
const {data, errors } = this.state;
return(
<Select
name={name}
value={data[name]}
label={label}
options={options}
onChange={this.handleChange}
error={errors[name]}
/>
);
};
}
export default Form
We define data and errors in state as a (user defined) naming convention
Using Form as base class
import Form from './common/form';
class LoginForm extends Form{
state = {
data:{username:"", password:""},
errors:{}
};
schema = {
username : Joi.string().required().label('Username'),
password : Joi.string().required().label('Password')
};
doSubmit = () => {
// call the server
// console.log("Submitted");
}
render(){
return (
<div>
<h1> Login </h1>
<form onSubmit={this.handleSubmit}>
{this.renderInput('username', 'Username')}
{this.renderInput('password', 'Password', 'password')}
{this.renderButton('Login')}
</form>
</div>
);
}
}
Input.jsx
import React from "react";
const Input = ({name, label, value, error, onChange, type}) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input
value={value}
onChange={onChange}
id={name}
name={name}
type={type}
className="form-control"
/>
{error && <div className="alert alert-danger">{error}</div>}
</div>
);
};
export defaiult Input;
Refactored Input.jsx
import React from "react";
const Input = ({name, label, error, ...rest}) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input
{...rest}
id={name}
name={name}
className="form-control"
/>
{error && <div className="alert alert-danger">{error}</div>}
</div>
);
};
export defaiult Input;
Select.jsx
import React from "react";
const Select = ({name, label, options, error, ...rest}) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<select name={name} id={name} {...rest} className="form-control">
<option value="" />
{options.map(option => (
<option key={option._id} value={option._id}> {option.name} </option>
))};
<select/>
{error && <div className="alert alert-danger">{error}</div>}
</div>
);
};
export defaiult Select;