Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A React hook 🎣 for easy form handling

License

NotificationsYou must be signed in to change notification settings

balazsorban44/use-form

Repository files navigation

A React hook 🎣 for easy form handling


Build StatusCode CoverageversiondownloadsTotal downloadsMIT LicensePRs WelcomeCode of Conduct

Watch on GitHubStar on GitHub


Table of Contents

Installation

This should be installed as one of your projectdependencies:

yarn add another-use-form-hook

or

npm install --save another-use-form-hook

NOTE:another-use-form-hook only works withreact >=16.8, since it is a hook.

Usage

This hook is intended to give a full solution for handling forms. From interdependent field value validations (meaning if a field value is dependent on other field value), to submitting the form, and providing information about when the UI should be unresponsive (loading of some kind of async-like operation), in addition to notification "hooks" to be able to inform the users the most efficient way.

To retrieve props for an input field, you have the following options:

  1. Using theform.inputs.{inputType}('name') input prop generator function (inputTypes is one ofthese)
  2. Usingform.fields.{name}.{value|error} andform.handleChange functions

NOTE: The example below is available live atCodeSandbox

Let's see a complex example to understand how it works:

importReactfrom"react";importReactDOMfrom"react-dom";importuseFormfrom"another-use-form-hook";import"./styles.css";/** * NOTE: We are using date-fns for this example, * but it is absolutly not a requirement. */import{addDays,isAfter,differenceInDays,parseISO}from"date-fns";constisValidEmail=email=>/\w*@\w*\.\w*/.test(email);// Do better 💩constTODAY=newDate();constApp=()=>{constform=useForm({initialState:{email:"",arrival:TODAY,departure:TODAY},validators:(fields,isSubmitting)=>({// if not submitting, don't throw errors for invalid e-mail, like an empty fieldemail:isSubmitting        ?isValidEmail(fields.email)        :typeoffields.email==="string",// earliest arrival must be tomorrowarrival:isAfter(parseISO(fields.arrival),TODAY),// earliest departure must be after tomorrowdeparture:isAfter(addDays(parseISO(fields.departure),1),TODAY),// departure must be at least a day after arrivalminOneNight:isSubmitting        ?differenceInDays(parseISO(fields.departure),parseISO(fields.arrival))>=1        :true}),onNotify:(type,reason)=>{// you can use type and reason to send specific notifications to the userswitch(type){case"submitError":console.error("Form could not be submitted: ",reason);break;case"submitSuccess":console.info("Form has been submitted.",reason);break;case"validationErrors":console.warn("The following problems occurred while validating: ",reason);break;default:break;}},onSubmit:async({ fields, setLoading, notify})=>{try{setLoading(true);// submitting the form, eg.: fetch("path/to/my/submit")constresponse=awaitnewPromise(resolve=>{setTimeout(()=>{console.log("Submitting: ",fields);resolve(fields);},1000);});notify("submitSuccess",response);}catch(error){notify("submitError",error.message);}finally{setLoading(false);}}});return(<formonSubmit={form.handleSubmit}>{/* option 1 (control all the props with a one-liner)*/}<fieldset><legend>Option 1</legend><labelhtmlFor="email">{form.fields.email.error ?"Invalid" :""} email</label><input{...form.inputs.email("email")}/><labelhtmlFor="departure">{form.fields.arrival.error ?"Invalid" :""} arrival</label><input{...form.inputs.date("arrival")}// You can override props by simply defining them lastonChange={e=>form.handleChange(e,["minOneNight"])}/></fieldset>{/* option 2 specify id, type, name, value props manually*/}<fieldset><legend>Option 2</legend><labelhtmlFor="arrival">{form.fields.arrival.error ?"Invalid" :""} arrival</label><inputtype="date"id="arrival"name="arrival"value={form.fields.arrival.value}onChange={e=>form.handleChange(e,["minOneNight"])}/><labelhtmlFor="departure">{form.fields.departure.error ?"Invalid" :""} departure</label><inputtype="date"id="departure"name="departure"value={form.fields.departure.value}onChange={e=>form.handleChange(e,["minOneNight"])}/></fieldset>{/* also from option 1 */}<button{...form.inputs.submit()}disabled={form.loading}>        Submit</button></form>);};ReactDOM.render(<App/>,document.querySelector("#root"));

Documentation

useForm

useForm(useFormParams:UserFormParams):UseForm

UseFormParams

nametypedescription
namestringRefer to one of the forms informProviderProps
initialStateobjectSeeInitialState
validatorsfunctionSeeValidators
onSubmitfunctionSeeSubmitCallback
onNotifyfunctionSeeNotifyCallback
InitialState

An object containing the default value of every field in a form.

Example:

useForm({initialState:{email:"",name:"",address:"",age:0}})

Ifname is defined, you can refer toinitialStates.{name} informProviderProps.

Example:

//...<FormProviderinitialStates={{login:{email:"email@example.com",password:""}}}>//...const form = useForm({name:"login"})console.log(form.fields.email.value) // email@example.com
Validators

This function is invoked beforeonChange andonSubmit. The former only runs the validations for the changed fields, while the latter runs it on the whole form. For convenience, it is also returned fromuseForm.

functionvalidators(fields:object,isSubmitting:boolean):Validations
nametypedescription
fieldsobjectAn object with the same shape asinitialState
submittingbooleanSet totrue, when called beforehandleSubmit
validationsobjectSeeValidations
Validations

An object containingboolean expressions. Each input field must have at least a corresponding property in this object, but you can define custom ones as well.

Example:

{email:submitting    ?isValidEmail(fields.email)    :typeoffields.email==="string"}

You can also look at thelive example.

SubmitCallback

Invoked whenhandleSubmit is called and there were no validation issues.

functiononSubmit(onSubmitParams:OnSubmitParams):void
nametypedescription
namestringSame asname inuseFormParams
fieldsobjectValidated fields, same shape asinitialState
setLoadingfunctionSets the returnedloading property ofuseForm
notifyfunctionSeeNotifyCallback
NotifyCallback

Invoked if there is a validation error when callinghandleChange orhandleSubmit. Can be manually triggered ononSubmit by callingnotify.

typeNotifyType="validationErrors"|"submitError"|"submitSuccess"functionnotify(type:NotifyType,reason:any):void
nametypedescription
typestringType of notification
reasonanyWhen type isvalidationErrors, it is a list of field names, Otherwise you set it to whatever you want.

Example:

Look at thelive example.

UseFormReturn

nametypedescription
namestringSame as inuseFormParams.
fieldsobjectSeeFieldValuesAndErrors
hasErrorsbooleanFor convenience.true if any of the returned fields.{name}.error istrue.
handleChangefunctionSeeChangeHandler
handleSubmitfunctionSeeSubmitHandler
loadingbooleanControlled bysetLoading inonSubmit
inputsobjectSeeInputPropGenerators
validatorsfunctionSeeValidators
FieldValuesAndErrors

Validated field values and their errors.

Example:

constform=useForm({initialState:{email:"email@example.com",age:-2}})console.log(form.fields)// {//  email: {//   value: "email@example.com",//   error: false//  },//  age: {//   value: -2,//   error: truefields//  }// }console.log(form.hasErrors)// true
ChangeHandler

You can call this two ways. Either pass an event as the first argument, or a partialfields object. With the latter, you can change multiple values at the same time. E.g.: resetting the form after submit, or any other reason you might have.

functionhandleChange(event:React.FormEvent,validations:string[]):voidfunctionhandleChange(fields:object,validations:string[]):void
nametypedescription
eventReact.FormEventStandard event. Usingtarget.{name|value|checked} to infer the intention
fieldsobjectPass a partialfields object, if you want to change multiple values at the same time
validationsstring[]Whichvalidators you would like to run. If omitted, only validators with the same event/field name will be run

Example:

Look at thelive example.

SubmitHandler

Call to submit the form. BeforeonSubmit is invoked,validators is run for every form field. If there were any errors,notify is invoked withtype beingvalidationErrors, andreason a list of form field names.

functionhandleSubmit():void
InputPropGenerators

An object, containing properties with the same name as theHTML input types, with some minor differences.

For convenience, sincedatetime-local contains a hyphen (-) character, it is also exposed asdatetimeLocal, to overcome the need of" characters, when accessing it.I.e.:

constform=useForm()form.inputs.datetimeLocal==form.inputs["datetime-local"]

In addition to the standard input types, there is aselect type also available.

Each property is a function:

functioninputType(name:string,options:InputTypeOptions):InputPropGeneratorsReturn
nametypedescription
namestringThe name of a field. Same as the properties ofinitialState
optionsobjectSeeInputTypeOptions

Example:

For examples of all types, you can checkthis test suite

InputTypeOptions

An optional object

nametypedescription
valuestringUsually, when using radio buttons, values are static. (Each button in the same group must have different values)
generatePropsfunctionProvidesname,value anderror that can be used to generate additional props. Useful, if you want to avoid usingform.fields
formNamestringIf the input type issubmit, it can be used to override the name of the form being submitted.

Example:

constform=useForm(/*...*/)// *click on option-2*console.log(form.fields.radio.value)// option-2return(<radiogroup><input{...inputs.radio('radio',{value:'option-1'})}/><input{...inputs.radio('radio',{value:'option-2'})}/>{/*You can do it the "traditional" way also*/}<input{...inputs.radio('radio')}value='option-3'/></radiogroup>)
constform=useForm(/*...*/)return(<div><input{...form.inputs.email("emailField",{generateProps:({error})=>({className:error ?"email-error" :"email",})})}/>{/*Tip: if your custom Input component takes an error prop, you can try this: */}<Input{...form.inputs.email("emailField",{generateProps:_=>_})}/>{/* This will spread error to Input as well.*/}{/*Or here is a more complex example for a custom Input component*/}<Input{...form.inputs.email("emailField",{generateProps:({error, name})=>({          error,label:error ?`Invalid${name}` :name,placeholder:`Type${name}`})})}/></div>)
InputPropGeneratorsReturn

An object that can be spread on a React input like element.

nametypedescription
namestringThe name of a field. Must be one of the properties ininitialState
idstringBy default, same asname. If input type isradio, it is the same asvalue, to avoid problems in radio groups whereid would be the same
valueanyThe value of the field
onChangefunctionSeeonChange
checkedbooleanIf input type ischeckbox, it is the same asvalue
onClickfunctionIf input type issubmit, it isonSubmit

Example:

constform=useForm(/*...*/)return(<div><labelhtmlFor="emailField">E-mail</label><input{...form.inputs.email("emailField")}/>{/* This is the same as */}<inputname="emailField"id="emailField"type="email"onChange={form.handleChange}value={form.fields.emailField.value}/></div>)

FormProvider

functionFormProvider(formProviderProps:FormProviderProps):JSX.Element

FormProviderProps

nametypedescription
initialStatesobjectSingle place to define allinitialState for every form. SeeInitialState
validatorsobjectSingle place to define allvalidators for every form. SeeValidators
onSubmitfunctionSame asonSubmit
onNotifyfunctionSame asonNotify
childrenReactElementThe element you would like to wrap withFormProvider

getForms

functiongetForms() :Forms

Forms

An object containing all the forms' current values inFormProvider. Same shape asinitialStates.


LICENSE

MIT

About

A React hook 🎣 for easy form handling

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp