From a2edafdc22325bf40aabd74ea8cba2da99550f5f Mon Sep 17 00:00:00 2001 From: Divlo Date: Fri, 26 Aug 2022 00:30:54 +0200 Subject: [PATCH] fix(hooks): usage of useForm --- .editorconfig | 6 ++- README.md | 21 ++++++----- example/index.tsx | 77 +++++++++++++------------------------- example/package-lock.json | 16 ++++++++ example/package.json | 5 ++- src/hooks/useForm.ts | 79 ++++++++++++++++++++------------------- src/utils/ajv.ts | 3 +- 7 files changed, 103 insertions(+), 104 deletions(-) diff --git a/.editorconfig b/.editorconfig index 9d08a1a..1103496 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,11 @@ +# For more information see: https://editorconfig.org/ + root = true [*] -charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf -insert_final_newline = true +charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true diff --git a/README.md b/README.md index cc98645..8da7aa2 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ import React from 'react' import { Form } from 'react-component-form' import type { HandleForm } from 'react-component-form' -const Example = () => { +export const Example = () => { const handleSubmit: HandleForm = (formData, formElement) => { console.log(formData) // { inputName: 'value of the input' } formElement.reset() @@ -72,27 +72,30 @@ You can see a more detailled example in the [./example](./example) folder. ```tsx import React from 'react' import { Form, useForm } from 'react-component-form' -import type { HandleSubmitCallback } from 'react-component-form' +import type { HandleUseFormCallback } from 'react-component-form' const schema = { inputName: { type: 'string', - required: true, minLength: 3, - maxLength: 10 + maxLength: 20 } } -const Example = () => { - const { errors, handleSubmit } = useForm(schema) +export const Example = () => { + const { errors, handleUseForm } = useForm(schema) - const onSubmit: HandleSubmitCallback = (formData, formElement) => { - console.log(formData) // { inputName: 'value of the input' } + const onSubmit: HandleUseFormCallback = ( + formData, + formElement + ) => { + console.log(formData) // { inputName: 'value of the input validated' } formElement.reset() + return null } return ( -
+ {errors.inputName != null &&

{errors.inputName[0].message}

} diff --git a/example/index.tsx b/example/index.tsx index 8890f3f..f12b02f 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -1,65 +1,38 @@ -import React from 'react' 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' -import GitHubLogo from 'url:./github.jpg' - -const App: React.FC = () => { - const handleSubmit: HandleForm = (formData, formElement) => { - console.clear() - console.log('onSubmit: ', formData) - formElement.reset() +const schema = { + inputName: { + type: 'string', + minLength: 3, + maxLength: 20 } +} - const handleChange: HandleForm = (formData) => { - console.log('onChange: ', formData) +export const Example = () => { + const { errors, handleUseForm } = useForm(schema) + + const onSubmit: HandleUseFormCallback = ( + formData, + formElement + ) => { + console.log(formData) // { inputName: 'value of the input validated' } + formElement.reset() + return null } return ( -
-

{''}

-
npm install --save react-component-form
+ + + {errors.inputName != null &&

{errors.inputName[0].message}

} - -
- - -
- - - - -
-

- Try the form and Inspect the console{' '} - - 😃 - -

-
- -
- - github - -
-
+ + ) } const container = document.getElementById('root') as HTMLElement const root = createRoot(container) -root.render() +root.render() diff --git a/example/package-lock.json b/example/package-lock.json index f52c618..48dc4f3 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -15,6 +15,7 @@ "@types/react": "18.0.17", "@types/react-dom": "18.0.6", "parcel": "2.7.0", + "process": "^0.11.10", "typescript": "4.7.4" } }, @@ -2505,6 +2506,15 @@ "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": { "resolved": "../node_modules/react", "link": true @@ -4350,6 +4360,12 @@ "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": { "version": "file:../node_modules/react", "requires": { diff --git a/example/package.json b/example/package.json index a5b2919..254856d 100644 --- a/example/package.json +++ b/example/package.json @@ -8,14 +8,15 @@ }, "dependencies": { "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": { "@parcel/transformer-image": "2.7.0", "@types/react": "18.0.17", "@types/react-dom": "18.0.6", "parcel": "2.7.0", + "process": "^0.11.10", "typescript": "4.7.4" } } diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index 2613c23..0b1f564 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -1,5 +1,5 @@ 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 { HandleForm } from '../components/Form' @@ -8,19 +8,21 @@ import { ajv } from '../utils/ajv' import { handleCheckboxBoolean } from '../utils/handleCheckboxBoolean' import { handleOptionalEmptyStringToNull } from '../utils/handleOptionalEmptyStringToNull' +export type Schema = SchemaOptions + export type Error = ErrorObject -export type ErrorsObject = { +export type ErrorsObject = { [key in keyof Partial]: Error[] | undefined } -export type HandleSubmitCallback = ( +export type HandleUseFormCallback = ( formData: Static>, formElement: HTMLFormElement -) => Promise | null> +) => Promise | null> | Message | null -export type HandleSubmit = ( - callback: HandleSubmitCallback +export type HandleUseForm = ( + callback?: HandleUseFormCallback ) => HandleForm export interface GlobalMessage { @@ -29,18 +31,16 @@ export interface GlobalMessage { properties?: undefined } -export interface PropertiesMessage { +export interface PropertiesMessage { type: 'error' value?: string properties: { [key in keyof Partial]: string } } -export type Message = - | GlobalMessage - | PropertiesMessage +export type Message = GlobalMessage | PropertiesMessage -export interface UseFormResult { - handleSubmit: HandleSubmit +export interface UseFormResult { + handleUseForm: HandleUseForm readonly fetchState: FetchState setFetchState: React.Dispatch> @@ -61,7 +61,7 @@ export interface UseFormResult { readonly errors: ErrorsObject } -export const useForm = ( +export const useForm = ( validationSchema: K ): UseFormResult => { const validationSchemaObject = useMemo(() => { @@ -78,7 +78,7 @@ export const useForm = ( return ajv.compile(validationSchemaObject) }, [validationSchemaObject]) - const handleSubmit: HandleSubmit = (callback) => { + const handleUseForm: HandleUseForm = (callback) => { return async (formData, formElement) => { setErrors({} as any) setMessage(null) @@ -103,30 +103,33 @@ export const useForm = ( setErrors(errors) } else { setErrors({} as any) - setFetchState('loading') - const message = await callback( - formData as Static>, - formElement - ) - if (message != null) { - const { value = null, type, properties } = message - setMessage(value) - setFetchState(type) - if (type === 'error') { - const propertiesErrors: ErrorsObject = - {} as any - for (const property in properties) { - propertiesErrors[property] = [ - { - keyword: 'message', - message: properties[property], - instancePath: `/${property}`, - schemaPath: `#/properties/${property}/message`, - params: {} - } - ] + if (callback != null) { + setFetchState('loading') + const message = await callback( + formData as Static>, + formElement + ) + if (message != null) { + const { value = null, type, properties } = message + setMessage(value) + setFetchState(type) + if (type === 'error') { + const propertiesErrors: ErrorsObject = + {} as any + for (const property in properties) { + propertiesErrors[property] = [ + { + keyword: 'message', + message: properties[property], + instancePath: `/${property}`, + schemaPath: `#/properties/${property}/message`, + params: {}, + data: formData[property] + } + ] + } + setErrors(propertiesErrors) } - setErrors(propertiesErrors) } } } @@ -134,7 +137,7 @@ export const useForm = ( } return { - handleSubmit, + handleUseForm, fetchState, setFetchState, message, diff --git a/src/utils/ajv.ts b/src/utils/ajv.ts index e97a6a6..ce46ae2 100644 --- a/src/utils/ajv.ts +++ b/src/utils/ajv.ts @@ -3,7 +3,8 @@ import Ajv from 'ajv' export const ajv = addFormats( new Ajv({ - allErrors: true + allErrors: true, + verbose: true }), [ 'date-time',