Skip to content

Instantly share code, notes, and snippets.

@PaGury
Last active March 18, 2021 09:15
Show Gist options
  • Save PaGury/1b5b4d5df13d4fdf520cea8b89553cc2 to your computer and use it in GitHub Desktop.
Save PaGury/1b5b4d5df13d4fdf520cea8b89553cc2 to your computer and use it in GitHub Desktop.
type FormValues = {
email: string;
password: string;
passwordMatch: string;
};
export const Signup = () => {
const tAuth = useTranslations('auth');
const tForm = useTranslations('tranverse');
const { mutate, isLoading, isError } = useRegisterMutation();
const { handleSubmit, watch, components } = useAppForm<FormValues>();
const {
Input,
} = components;
const onSubmit = useCallback((values: FormValues) => {
mutate({
input: {
password: values.password,
email: values.email,
username: 'empty',
}
});
}, []);
const password = useRef({});
password.current = watch('password', '');
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}>
<form onSubmit={handleSubmit(onSubmit)}>
<Title bold ultra>{tAuth.translate('SignupTitle')}</Title>
<Margin top={10}>
<Text type={TypographyType.secondary}>{tAuth.translate('FillYourSignupInformations')}</Text>
</Margin>
<Margin top={50}>
<Input
autoFocus
noMargin
name="email"
options={{ required: true, pattern: emailRegex }}
text={tForm.getInputText('form.email')}
/>
</Margin>
<Input
name="password"
options={{ required: true, validate: passwordMatcher }}
text={tForm.getInputText('form.password')}
inputProps={{ type: 'password' }}
/>
<Input
name="passwordMatch"
options={{ required: true, validate: value => value === password.current }}
text={tForm.getInputText('form.passwordMatch')}
inputProps={{ type: 'password' }}
/>
<Margin align="right">
<Link to="/signin">Connectez-vous</Link>
</Margin>
<Margin top={30} align="right">
<Button isLoading={isLoading} type="submit">S'inscrire</Button>
</Margin>
<Margin top={40} bottom={30} align="center" className={$.SocialConnectorSpacer}>
<Text small ultra type={TypographyType.secondary}>ou s'inscrire avec</Text>
</Margin>
<SocialConnectors />
</form>
</motion.div>
);
};
import React, { useCallback } from "react";
import { FieldErrors, RegisterOptions, useForm, UseFormMethods } from "react-hook-form";
import { Margin } from "@Components/Global/Gabarit/Gabarit";
import { Label } from "@Components/Global/Label/Label";
import { Input as RawInput } from "@Components/Global/Input/Input";
type AnyFormValue = Record<string, any>;
type RenderProps<FormValues extends AnyFormValue, ExtendedProps extends {}> = {
hasError: boolean;
autoFocus?: boolean;
name: keyof FormValues;
options?: RegisterOptions;
register: UseFormMethods<FormValues>['register'];
text: {
label: string;
error?: string;
placeholder?: string;
hint?: string;
};
inputProps?: ExtendedProps;
};
type FormControllerProps<FormValues extends AnyFormValue, ExtendedProps extends {}> = {
autoFocus?: boolean;
name: keyof FormValues;
noMargin?: boolean;
register: UseFormMethods<FormValues>['register'];
errors: FieldErrors<FormValues>;
options?: RegisterOptions;
render: (props: RenderProps<FormValues, ExtendedProps>) => JSX.Element;
text: {
label: string;
error?: string;
placeholder?: string;
hint?: string;
};
inputProps?: ExtendedProps;
};
const FormController = <FormValues extends AnyFormValue, ExtendedProps extends {}>
(props: FormControllerProps<FormValues, ExtendedProps>) => {
const {
errors,
name,
register,
render,
text,
autoFocus,
noMargin,
options = {},
inputProps,
} = props;
const {
label,
error,
hint,
} = text;
const hasError = !!errors[name];
const errorMessage = hasError ? error : undefined;
return (
<Margin top={noMargin ? 0 : 15}>
<Label
required={!!options.required}
error={errorMessage}
hint={hint}
input={
render({
hasError,
name,
register,
text,
autoFocus,
options,
inputProps,
})
}>
{label}
</Label>
</Margin>
);
};
export const useFormController = <T extends AnyFormValue, ExtendedProps extends {}>(
register: UseFormMethods<T>['register'],
errors: UseFormMethods<T>['errors'],
render: FormControllerProps<T, ExtendedProps>['render'],
) => {
type RawProps = FormControllerProps<AnyFormValue, ExtendedProps>;
type OmitKeys = 'render' | 'register' | 'errors';
const registerRef = React.useRef(register);
registerRef.current = register;
const errorsRef = React.useRef(errors);
errorsRef.current = errors;
const renderRef = React.useRef(render);
renderRef.current = render;
type Props = Omit<RawProps, OmitKeys>;
const PrefilledFormController = useCallback((props: Props) => (
<FormController
render={render}
register={registerRef.current}
errors={errorsRef.current}
{...props}
/>
), []);
return PrefilledFormController;
};
export const useAppForm = <T extends AnyFormValue>() => {
const form = useForm<T>();
type ExtendedInputProps = {
type: 'text' | 'password';
};
const Input = useFormController<T, ExtendedInputProps>(
form.register,
form.errors,
({ name, autoFocus, hasError, register, options, text: { placeholder }, inputProps }) => (
<RawInput
autoFocus={autoFocus}
hasError={hasError}
name={name as string}
ref={register(options)}
placeholder={placeholder}
type={inputProps?.type ?? 'text'}
/>
)
);
return {
...form,
components: {
Input,
},
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment