- Notifications
You must be signed in to change notification settings - Fork1
jotaijs/jotai-form
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
👻📃
The library can be simply installed by addingjotai-form to your dependencies
npm i jotai-form# oryarn add jotai-form# orpnpm add jotai-form
Note: The library depends on
jotaiso please make sure you have jotaiinstalled, if not you can do so by using the below commandsnpm i jotai
The very basic form of validation and the most common use case will involveusingatomWithValidate, which creates a similar atom tojotai
Thisatom can be used with theuseAtom helper but with a slight change. Thechange is that instead of directly giving you the state value you now have formbased properties
constform={ value,// the current value of the state isDirty,// boolean value based on if the value has changed from the initial value isValid,// boolean value representing if the current atom state is valid error,// if an error was thrown from the validator, that'll be available here// and a conditional// boolean value when working with async validation, this// will be `true` for the duration of the validation and `false` once the validation is complete isValidating,};
Note:
isValidatingwill not be a property if you are using a synchronousvalidation function, your IDE should already help you with this but use thisproperty only when using asynchronous validation functions.
An example, combining all that information. You can find more such examples intheexamples folder
import{useAtom}from'jotai';import{atomWithValidate}from'jotai-form';import*asYupfrom'yup';// defining a validation schema for the atomconstemailSchema=Yup.string().email().required();// creating the atom with an async validation functionconstemailAtom=atomWithValidate('',{validate:async(v)=>{awaitemailSchema.validate(v);returnv;},});constForm=()=>{// Same API as other jotai atom's with the difference being// addition of form properties on `email`const[email,setEmail]=useAtom(emailAtom);return(<div><inputvalue={email.value}onChange={(e)=>setEmail(e.target.value)}/><span>{email.isValid&&'Valid'}</span><span>{!email.isValid&&`${email.error}`}</span></div>);};
You might have cases where you would like to validate the combination ofmultiple values.
Example, Maybe the combined name length of first and last names cannot exceed 15characters.
For this you can usevalidateAtoms which takes in a atom group and a validatorfunction which works very similar to theatomWithValidate's validate function.
The atom group validation atom is a read only atom and with similar propertiesas the atom returned fromatomWithValidate. The only change beingvaluesinstead ofvalue. This contains the passed atom group's values
Note: In most cases the
atomWithValidateshould suffice, form validationcomes into the picture when dealing with fields that do need a finalvalidation, the example here is one of the many cases but do not abuse formlevel validation.
Note:
validateAtomswill not trigger each atom's internal validatefunctions, while adding disabled states or any other case where you need tocheck the atom's validity, please use the atom's ownisValidanderrorproperties.
import{useAtom}from'jotai';import{atomWithValidate,validateAtoms}from'jotai-form';import*asYupfrom'yup';constnameSchema=Yup.string().required();constfirstNameAtom=atomWithValidate('',{validate:async(v)=>{awaitnameSchema.validate(v);returnv;},});constlastNameAtom=atomWithValidate('',{validate:async(v)=>{awaitnameSchema.validate(v);returnv;},});constformGroup=validateAtoms({firstName:firstNameAtom,lastName:lastNameAtom,},(values)=>{if(values.firstName.length+values.lastName.length>15){thrownewError("Overall name can't be longer than 15 characters");}},);constForm=()=>{const[firstName,setFirstName]=useAtom(firstNameAtom);const[lastName,setLastName]=useAtom(lastNameAtom);const[formState]=useAtom(formGroup);return(<div><label> firstName<inputvalue={firstName.value}onChange={(e)=>setFirstName(e.target.value)}/><span>{firstName.isValid&&'Valid'}</span><span>{!firstName.isValid&&`${email.error}`}</span></label><label> lastName<inputvalue={lastName.value}onChange={(e)=>setLastName(e.target.value)}/><span>{lastName.isValid&&'Valid'}</span><span>{!lastName.isValid&&`${email.error}`}</span></label><buttondisabled={!formState.isValid||!firstName.isValid||!lastName.isValid}> Submit</button>{// or// <button disabled={!(formState.isValid && firstName.isValid && lastName.isValid)}>Submit</button>}<span>{!formState.isValid&&`${formState.error}`}</span></div>);};
Added inv0.1.1
Form Group Validation usingvalidateAtoms is easier when the form is tinier and doesn't need much, but having multiple such hooks for a larger group of atoms might not be easy and lead to a lot of hooks.
We would like to keep the atomic approach but in case of forms it does need a little extension and so you can do so withatomWithFormControls.
Here's an example of what an example of this would look like.
Notes
atomWithFormControlsstill expects aatomWithValidategroup to be passed to it, this is so that you can still move and combine the same atom with otherform control groups if needed.
import{useAtom}from'jotai';import{atomWithValidate,validateAtoms}from'jotai-form';import*asYupfrom'yup';constnameSchema=Yup.string().required();constfirstNameAtom=atomWithValidate('',{validate:async(v)=>{awaitnameSchema.validate(v);returnv;},});constlastNameAtom=atomWithValidate('',{validate:async(v)=>{awaitnameSchema.validate(v);returnv;},});constformControlAtom=atomWithFormControls({firstName:firstNameAtom,lastName:lastNameAtom,},{validate:(values)=>{if(values.firstName!='jotai'){thrownewError("Oh well, can't let you in");}},},);functionFormComponent(){const{// Values per field values,// is the form valid isValid,// focused state per field focused,// touched state per field touched,// errors per field fieldErrors,// form error error,// handle change of value per field handleOnChange,// handle blur event per field handleOnBlur,// handle focus event per field handleOnFocus,}=useAtomValue(formControlAtom);return(<><form><div><inputvalue={values.firstName}onChange={(e)=>{handleOnChange('firstName')(e.target.value);}}onFocus={handleOnFocus('firstName')}onBlur={handleOnBlur('firstName')}/><p>{fieldErrors.firstName&&touched.firstName ?`${fieldErrors.firstName}` :'Valid'}</p></div><div><inputvalue={values.lastName}onChange={(e)=>{handleOnChange('lastName')(e.target.value);}}onFocus={handleOnFocus('lastName')}onBlur={handleOnBlur('lastName')}/><p>{fieldErrors.firstName&&touched.firstName ?`${fieldErrors.firstName}` :'Valid'}</p></div><p>isValid:{isValid ?'Valid' :"Something's wrong"}</p><p>Form Error:{error?.toString()}</p></form></>);}
atomWithValidate is the primary base for create validating atoms and it's state and validations are inferred by the other atoms in most cases.
atomWithValidate(initialValue,options);
| param | description | default |
|---|---|---|
initialValue | The initial value of the atom | undefined |
options.validate | function returning value orthrows an error | undefined |
| receives the current value of the atom.more... | ||
options.areEqual | function to compare the initial value to the changed values | Object.is |
ThevalidateAtoms function accepts a group of primitives, in this caseatomWithValidate and a validator function for the entire group
validateAtoms(atomGroup,validator);
| param | description | default |
|---|---|---|
atomGroup | an object ofatomsWithValidate atoms | undefined |
| the keys of the atomGroup are used as names/labels | ||
when passed to thevalidator function | ||
validator | function returning void orthrows an error | undefined |
| receives the atomGroup's values with the same keys.more... |
atomWithFormControls(atomGroup,options);
| param | description | default |
|---|---|---|
atomGroup | an object ofatomsWithValidate atoms | undefined |
| the keys of the atomGroup are used as names/labels | ||
when passed to thevalidator function | ||
options.validate | function returning void orthrows an error | undefined |
| receives the atomGroup's values with the same keys.more... |
While the API for the validator functions,options.validate orvalidator are very similar there's a few differences and also let's talk abouthow they work and what's the different
Rules
Things that apply to all the validator functions
- If the function executes without any error, the validation was successful.
- If there's any error , even something raised from an inner function, it'llmake the form invalid.
- Can pass in a
async/ sync function for validation. - The function will be passed the value of the atom that they are supposed tovalidate.
options.validatefromatomWithValidatewill get the value for it's own atom andvalidator,options.validateforvalidateAtomsandatomWithFormControlswill get all the values from the passed inatomGroup)
Differences
Primitive atoms expects you to return the value at the end of thevalidation to make sure the validation was run for the same value and yourstate and validated state doesn't have any mismatch.
Curator Functions doesn't expect a return value since it acts as a listener to thegroup of atoms passed in and the values will not need the same state check, so you can just throw errors from it andif it doesn't return the values back there won't be a problem
Note: The keys of the object defined for the atomGroup is used as the keysfor the
valuesthat are passed to the atom curator functions, the same is applicableforatomWithFormControlsconstatomGroup={name:nameAtom,age:ageAtom};constvalidator=(values)=>{values.name;// nameAtom's valuevalues.age;// ageAtom's value};validateAtoms(atomGroup,validator);atomWithFormControls(atomGroup,{validate:validator,});
About
Form atoms for Jotai
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.