fix(hooks): usage of useForm

This commit is contained in:
Divlo 2022-08-26 00:30:54 +02:00
parent 17656c149a
commit a2edafdc22
No known key found for this signature in database
GPG Key ID: 8F9478F220CE65E9
7 changed files with 103 additions and 104 deletions

View File

@ -1,9 +1,11 @@
# For more information see: https://editorconfig.org/
root = true root = true
[*] [*]
charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
end_of_line = lf end_of_line = lf
insert_final_newline = true charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -41,7 +41,7 @@ import React from 'react'
import { Form } from 'react-component-form' import { Form } from 'react-component-form'
import type { HandleForm } from 'react-component-form' import type { HandleForm } from 'react-component-form'
const Example = () => { export const Example = () => {
const handleSubmit: HandleForm = (formData, formElement) => { const handleSubmit: HandleForm = (formData, formElement) => {
console.log(formData) // { inputName: 'value of the input' } console.log(formData) // { inputName: 'value of the input' }
formElement.reset() formElement.reset()
@ -72,27 +72,30 @@ You can see a more detailled example in the [./example](./example) folder.
```tsx ```tsx
import React from 'react' import React from 'react'
import { Form, useForm } from 'react-component-form' import { Form, useForm } from 'react-component-form'
import type { HandleSubmitCallback } from 'react-component-form' import type { HandleUseFormCallback } from 'react-component-form'
const schema = { const schema = {
inputName: { inputName: {
type: 'string', type: 'string',
required: true,
minLength: 3, minLength: 3,
maxLength: 10 maxLength: 20
} }
} }
const Example = () => { export const Example = () => {
const { errors, handleSubmit } = useForm(schema) const { errors, handleUseForm } = useForm(schema)
const onSubmit: HandleSubmitCallback<typeof schema> = (formData, formElement) => { const onSubmit: HandleUseFormCallback<typeof schema> = (
console.log(formData) // { inputName: 'value of the input' } formData,
formElement
) => {
console.log(formData) // { inputName: 'value of the input validated' }
formElement.reset() formElement.reset()
return null
} }
return ( return (
<Form onSubmit={handleSubmit(onSubmit)}> <Form onSubmit={handleUseForm(onSubmit)}>
<input type='text' name='inputName' /> <input type='text' name='inputName' />
{errors.inputName != null && <p>{errors.inputName[0].message}</p>} {errors.inputName != null && <p>{errors.inputName[0].message}</p>}

View File

@ -1,65 +1,38 @@
import React from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import { Form, HandleForm } from 'react-component-form' import React from 'react'
import { Form, useForm } from 'react-component-form'
import type { HandleUseFormCallback } from 'react-component-form'
import './index.css' const schema = {
import GitHubLogo from 'url:./github.jpg' inputName: {
type: 'string',
const App: React.FC = () => { minLength: 3,
const handleSubmit: HandleForm = (formData, formElement) => { maxLength: 20
console.clear()
console.log('onSubmit: ', formData)
formElement.reset()
} }
}
const handleChange: HandleForm = (formData) => { export const Example = () => {
console.log('onChange: ', formData) const { errors, handleUseForm } = useForm(schema)
const onSubmit: HandleUseFormCallback<typeof schema> = (
formData,
formElement
) => {
console.log(formData) // { inputName: 'value of the input validated' }
formElement.reset()
return null
} }
return ( return (
<div className='container'> <Form onSubmit={handleUseForm(onSubmit)}>
<h2>{'<Form />'}</h2> <input type='text' name='inputName' />
<h5 className='title-install'>npm install --save react-component-form</h5> {errors.inputName != null && <p>{errors.inputName[0].message}</p>}
<Form onSubmit={handleSubmit} onChange={handleChange}> <button type='submit'>Submit</button>
<div className='form-group'> </Form>
<label htmlFor='name'>Name :</label>
<input
className='form-control'
type='text'
name='name'
id='name'
placeholder='name'
/>
</div>
<button type='submit' className='btn btn-primary'>
Submit
</button>
</Form>
<div className='result-container'>
<h4>
Try the form and Inspect the console{' '}
<span role='img' aria-label='smiley'>
😃
</span>
</h4>
</div>
<div className='github-logo'>
<a
target='_blank'
rel='noopener noreferrer'
href='https://github.com/Divlo/react-component-form'
>
<img width='30px' alt='github' src={GitHubLogo} />
</a>
</div>
</div>
) )
} }
const container = document.getElementById('root') as HTMLElement const container = document.getElementById('root') as HTMLElement
const root = createRoot(container) const root = createRoot(container)
root.render(<App />) root.render(<Example />)

View File

@ -15,6 +15,7 @@
"@types/react": "18.0.17", "@types/react": "18.0.17",
"@types/react-dom": "18.0.6", "@types/react-dom": "18.0.6",
"parcel": "2.7.0", "parcel": "2.7.0",
"process": "^0.11.10",
"typescript": "4.7.4" "typescript": "4.7.4"
} }
}, },
@ -2505,6 +2506,15 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"dev": true,
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/react": { "node_modules/react": {
"resolved": "../node_modules/react", "resolved": "../node_modules/react",
"link": true "link": true
@ -4350,6 +4360,12 @@
"is-json": "^2.0.1" "is-json": "^2.0.1"
} }
}, },
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"dev": true
},
"react": { "react": {
"version": "file:../node_modules/react", "version": "file:../node_modules/react",
"requires": { "requires": {

View File

@ -8,14 +8,15 @@
}, },
"dependencies": { "dependencies": {
"react": "file:../node_modules/react", "react": "file:../node_modules/react",
"react-dom": "file:../node_modules/react-dom", "react-component-form": "file:..",
"react-component-form": "file:.." "react-dom": "file:../node_modules/react-dom"
}, },
"devDependencies": { "devDependencies": {
"@parcel/transformer-image": "2.7.0", "@parcel/transformer-image": "2.7.0",
"@types/react": "18.0.17", "@types/react": "18.0.17",
"@types/react-dom": "18.0.6", "@types/react-dom": "18.0.6",
"parcel": "2.7.0", "parcel": "2.7.0",
"process": "^0.11.10",
"typescript": "4.7.4" "typescript": "4.7.4"
} }
} }

View File

@ -1,5 +1,5 @@
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { Static, TObject, TProperties, Type } from '@sinclair/typebox' import { SchemaOptions, Static, TObject, Type } from '@sinclair/typebox'
import type { ErrorObject } from 'ajv' import type { ErrorObject } from 'ajv'
import type { HandleForm } from '../components/Form' import type { HandleForm } from '../components/Form'
@ -8,19 +8,21 @@ import { ajv } from '../utils/ajv'
import { handleCheckboxBoolean } from '../utils/handleCheckboxBoolean' import { handleCheckboxBoolean } from '../utils/handleCheckboxBoolean'
import { handleOptionalEmptyStringToNull } from '../utils/handleOptionalEmptyStringToNull' import { handleOptionalEmptyStringToNull } from '../utils/handleOptionalEmptyStringToNull'
export type Schema = SchemaOptions
export type Error = ErrorObject export type Error = ErrorObject
export type ErrorsObject<K extends TProperties> = { export type ErrorsObject<K extends Schema> = {
[key in keyof Partial<K>]: Error[] | undefined [key in keyof Partial<K>]: Error[] | undefined
} }
export type HandleSubmitCallback<K extends TProperties> = ( export type HandleUseFormCallback<K extends Schema> = (
formData: Static<TObject<K>>, formData: Static<TObject<K>>,
formElement: HTMLFormElement formElement: HTMLFormElement
) => Promise<Message<K> | null> ) => Promise<Message<K> | null> | Message<K> | null
export type HandleSubmit<K extends TProperties> = ( export type HandleUseForm<K extends Schema> = (
callback: HandleSubmitCallback<K> callback?: HandleUseFormCallback<K>
) => HandleForm ) => HandleForm
export interface GlobalMessage { export interface GlobalMessage {
@ -29,18 +31,16 @@ export interface GlobalMessage {
properties?: undefined properties?: undefined
} }
export interface PropertiesMessage<K extends TProperties> { export interface PropertiesMessage<K extends Schema> {
type: 'error' type: 'error'
value?: string value?: string
properties: { [key in keyof Partial<K>]: string } properties: { [key in keyof Partial<K>]: string }
} }
export type Message<K extends TProperties> = export type Message<K extends Schema> = GlobalMessage | PropertiesMessage<K>
| GlobalMessage
| PropertiesMessage<K>
export interface UseFormResult<K extends TProperties> { export interface UseFormResult<K extends Schema> {
handleSubmit: HandleSubmit<K> handleUseForm: HandleUseForm<K>
readonly fetchState: FetchState readonly fetchState: FetchState
setFetchState: React.Dispatch<React.SetStateAction<FetchState>> setFetchState: React.Dispatch<React.SetStateAction<FetchState>>
@ -61,7 +61,7 @@ export interface UseFormResult<K extends TProperties> {
readonly errors: ErrorsObject<K> readonly errors: ErrorsObject<K>
} }
export const useForm = <K extends TProperties>( export const useForm = <K extends Schema>(
validationSchema: K validationSchema: K
): UseFormResult<typeof validationSchema> => { ): UseFormResult<typeof validationSchema> => {
const validationSchemaObject = useMemo(() => { const validationSchemaObject = useMemo(() => {
@ -78,7 +78,7 @@ export const useForm = <K extends TProperties>(
return ajv.compile(validationSchemaObject) return ajv.compile(validationSchemaObject)
}, [validationSchemaObject]) }, [validationSchemaObject])
const handleSubmit: HandleSubmit<typeof validationSchema> = (callback) => { const handleUseForm: HandleUseForm<typeof validationSchema> = (callback) => {
return async (formData, formElement) => { return async (formData, formElement) => {
setErrors({} as any) setErrors({} as any)
setMessage(null) setMessage(null)
@ -103,30 +103,33 @@ export const useForm = <K extends TProperties>(
setErrors(errors) setErrors(errors)
} else { } else {
setErrors({} as any) setErrors({} as any)
setFetchState('loading') if (callback != null) {
const message = await callback( setFetchState('loading')
formData as Static<TObject<typeof validationSchema>>, const message = await callback(
formElement formData as Static<TObject<typeof validationSchema>>,
) formElement
if (message != null) { )
const { value = null, type, properties } = message if (message != null) {
setMessage(value) const { value = null, type, properties } = message
setFetchState(type) setMessage(value)
if (type === 'error') { setFetchState(type)
const propertiesErrors: ErrorsObject<typeof validationSchema> = if (type === 'error') {
{} as any const propertiesErrors: ErrorsObject<typeof validationSchema> =
for (const property in properties) { {} as any
propertiesErrors[property] = [ for (const property in properties) {
{ propertiesErrors[property] = [
keyword: 'message', {
message: properties[property], keyword: 'message',
instancePath: `/${property}`, message: properties[property],
schemaPath: `#/properties/${property}/message`, instancePath: `/${property}`,
params: {} schemaPath: `#/properties/${property}/message`,
} params: {},
] data: formData[property]
}
]
}
setErrors(propertiesErrors)
} }
setErrors(propertiesErrors)
} }
} }
} }
@ -134,7 +137,7 @@ export const useForm = <K extends TProperties>(
} }
return { return {
handleSubmit, handleUseForm,
fetchState, fetchState,
setFetchState, setFetchState,
message, message,

View File

@ -3,7 +3,8 @@ import Ajv from 'ajv'
export const ajv = addFormats( export const ajv = addFormats(
new Ajv({ new Ajv({
allErrors: true allErrors: true,
verbose: true
}), }),
[ [
'date-time', 'date-time',