From b16fdef38978c05ba6ee0be61cbaba64f1acf272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=90=E8=97=A4=E6=98=AD=E6=96=87?= Date: Sun, 4 Sep 2022 20:37:46 +0900 Subject: [PATCH] examples: react-hook-form imporved render call --- .../react-hook-form/pages/form/[index].tsx | 65 +++++++------------ .../react-hook-form/src/hooks/useFormSync.ts | 59 +++++++++++++++++ 2 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 examples/react-hook-form/src/hooks/useFormSync.ts diff --git a/examples/react-hook-form/pages/form/[index].tsx b/examples/react-hook-form/pages/form/[index].tsx index 94832d6d..2125bba3 100755 --- a/examples/react-hook-form/pages/form/[index].tsx +++ b/examples/react-hook-form/pages/form/[index].tsx @@ -1,47 +1,40 @@ -import { string } from '@recoiljs/refine' +import { object, string } from '@recoiljs/refine' import Head from 'next/head' import { useRouter } from 'next/router' -import { useForm } from 'react-hook-form' import { SubmitHandler } from 'react-hook-form/dist/types/form' -import { atom, useRecoilState } from 'recoil' import { syncEffect } from 'recoil-sync' +import { initializableAtom } from 'recoil-sync-next' +import { useFormSync } from '../../src/hooks/useFormSync' import styles from '../../styles/form.module.css' import type { NextPage } from 'next' -const nameState = atom({ - key: 'nameState', - default: '', - effects: [syncEffect({ refine: string() })], -}) - -const commentState = atom({ - key: 'commentState', - default: '', - effects: [syncEffect({ refine: string() })], -}) - -type FormData = { +type FormState = { name: string comment: string } +const formState = initializableAtom({ + key: 'formState', + effects: [ + syncEffect({ + refine: object({ + name: string(), + comment: string(), + }), + }), + ], +}) + const Form: NextPage = () => { - // Every time recoil state is updated, rendering is called, - // but no DOM update is made if it is kept to un-controlling components. - const [name, setName] = useRecoilState(nameState) - const [comment, setComment] = useRecoilState(commentState) + // check render + console.log('Form: re render') - // form + const { register, onChangeForm, handleSubmit } = useFormSync( + formState({ name: 'a', comment: 'b' }) + ) const router = useRouter() - const { handleSubmit, register } = useForm({ - mode: 'onBlur', - defaultValues: { - name, - comment, - }, - }) - const onSubmit: SubmitHandler = async (data) => { + const onSubmit: SubmitHandler = async (data) => { console.log('submit data', data) await router.push('/form/success') } @@ -56,23 +49,15 @@ const Form: NextPage = () => {

Form

-
+
name
- setName(e.target.value)} - /> +
comment
- setComment(e.target.value)} - /> +
diff --git a/examples/react-hook-form/src/hooks/useFormSync.ts b/examples/react-hook-form/src/hooks/useFormSync.ts new file mode 100644 index 00000000..c26ce1df --- /dev/null +++ b/examples/react-hook-form/src/hooks/useFormSync.ts @@ -0,0 +1,59 @@ +import { useCallback, useRef } from 'react' +import { + DeepPartial, + FieldValues, + useForm, + UseFormProps, + UseFormReturn, +} from 'react-hook-form' +import { + RecoilState, + useRecoilCallback, + useResetRecoilState, + useSetRecoilState, +} from 'recoil' + +type UseFormSyncReturn< + TFieldValues extends FieldValues = FieldValues, + TContext = any +> = UseFormReturn & { onChangeForm: () => void } + +export function useFormSync< + TFieldValues extends FieldValues = FieldValues, + TContext = any +>( + formState: RecoilState, + props?: Omit, 'defaultValues'> +): UseFormSyncReturn { + const getDefaultValues = useRecoilCallback( + ({ snapshot }) => + () => { + return snapshot.getLoadable(formState).contents + }, + [] + ) + const defaultValuesRef = useRef>() + defaultValuesRef.current ??= getDefaultValues() + + const { + getValues, + reset: resetForm, + ...rest + } = useForm({ + ...props, + defaultValues: defaultValuesRef.current, + }) + + const setFormValues = useSetRecoilState(formState) + const onChangeForm = useCallback(() => { + setFormValues(getValues()) + }, [setFormValues, getValues]) + + const resetState = useResetRecoilState(formState) + const reset = useCallback(() => { + resetState() + resetForm(getDefaultValues()) + }, [getDefaultValues, resetForm, resetState]) + + return { ...rest, getValues, reset, onChangeForm } +}