Skip to content

Commit

Permalink
Make Form generic for better TS support (#5867)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe authored Jul 7, 2022
1 parent 92b3516 commit 9db899e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 52 deletions.
2 changes: 1 addition & 1 deletion packages/forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@babel/runtime-corejs3": "7.16.7",
"core-js": "3.23.3",
"pascalcase": "1.0.0",
"react-hook-form": "7.33.0"
"react-hook-form": "7.33.1"
},
"devDependencies": {
"@babel/cli": "7.16.7",
Expand Down
2 changes: 1 addition & 1 deletion packages/forms/src/FormError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ServerError extends Error {
result: Record<string, any>
}

interface RWGqlError {
export interface RWGqlError {
message: string
graphQLErrors: ReadonlyArray<GraphQLError>
networkError: Error | ServerParseError | ServerError | null
Expand Down
101 changes: 56 additions & 45 deletions packages/forms/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import {
UseFormProps,
} from 'react-hook-form'

import FormError from './FormError'
import FormError, { RWGqlError } from './FormError'

/**
* We slightly extend `react-hook-form`'s `RegisterOptions` to make working with GraphQL easier.
Expand Down Expand Up @@ -484,19 +484,20 @@ interface ServerErrorsContextProps {

const ServerErrorsContext = React.createContext({} as ServerErrorsContextProps)

export interface FormProps
export interface FormProps<TFieldValues>
extends Omit<React.ComponentPropsWithRef<'form'>, 'onSubmit'> {
error?: any
/**
* The methods returned by `useForm`.
* This props's only necessary if you've called `useForm` yourself to get access to one of it's functions, like `reset`.
* This prop is only necessary if you've called `useForm` yourself to get
* access to one of its functions, like `reset`.
*
* @example
*
* ```javascript
* const formMethods = useForm()
* ```typescript
* const formMethods = useForm<FormData>()
*
* const onSubmit = (data) => {
* const onSubmit = (data: FormData) => {
* sendDataToServer(data)
* formMethods.reset()
* }
Expand All @@ -506,7 +507,7 @@ export interface FormProps
* )
* ```
*/
formMethods?: UseFormReturn
formMethods?: UseFormReturn<TFieldValues>
/**
* Configures how React Hook Form performs validation, among other things.
*
Expand All @@ -518,50 +519,59 @@ export interface FormProps
*
* @see {@link https://react-hook-form.com/api/useform}
*/
config?: UseFormProps
onSubmit?: (
value: Record<string, any>,
event?: React.BaseSyntheticEvent
) => void
config?: UseFormProps<TFieldValues>
onSubmit?: (value: TFieldValues, event?: React.BaseSyntheticEvent) => void
}

/**
* Renders a `<form>` with the required context.
*/
const Form = forwardRef(
(
{
config,
error: errorProps,
formMethods: propFormMethods,
onSubmit,
children,
...rest
}: FormProps,
ref: ForwardedRef<HTMLFormElement>
) => {
const hookFormMethods = useForm(config)
const formMethods = propFormMethods || hookFormMethods
function FormInner<TFieldValues>(
{
config,
error: errorProps,
formMethods: propFormMethods,
onSubmit,
children,
...rest
}: FormProps<TFieldValues>,
ref: ForwardedRef<HTMLFormElement>
) {
const hookFormMethods = useForm<TFieldValues>(config)
const formMethods = propFormMethods || hookFormMethods

return (
<form
ref={ref}
{...rest}
onSubmit={formMethods.handleSubmit((data, event) =>
onSubmit?.(data, event)
)}
return (
<form
ref={ref}
{...rest}
onSubmit={formMethods.handleSubmit((data, event) =>
onSubmit?.(data, event)
)}
>
<ServerErrorsContext.Provider
value={
errorProps?.graphQLErrors[0]?.extensions?.properties?.messages || {}
}
>
<ServerErrorsContext.Provider
value={
errorProps?.graphQLErrors[0]?.extensions?.properties?.messages || {}
}
>
<FormProvider {...formMethods}>{children}</FormProvider>
</ServerErrorsContext.Provider>
</form>
)
}
)
<FormProvider {...formMethods}>{children}</FormProvider>
</ServerErrorsContext.Provider>
</form>
)
}

// Sorry about the `as` type assertion (type cast) here. Normally I'd redeclare
// forwardRef to only return a plain function, allowing us to use TypeScript's
// Higher-order Function Type Inference. But that gives us problems with the
// ForwardRefExoticComponent type we use for our InputComponents. So instead
// of changing that type (because it's correct) I use a type assertion here.
// forwardRef is notoriously difficult to use with UI component libs.
// Chakra-UI also says:
// > To be honest, the forwardRef type is quite complex [...] I'd recommend
// > that you cast the type
// https://github.com/chakra-ui/chakra-ui/issues/4528#issuecomment-902566185
const Form = forwardRef(FormInner) as <TFieldValues>(
props: FormProps<TFieldValues> & React.RefAttributes<HTMLFormElement>
) => React.ReactElement | null

export interface LabelProps
extends Pick<FieldProps, 'errorClassName' | 'errorStyle'>,
Expand Down Expand Up @@ -621,7 +631,7 @@ const DEFAULT_MESSAGES = {
*
* @example Displaying a validation error message with `<FieldError>`
*
* `<FieldError>` doesnt render (i.e. returns `null`) when there's no error on `<TextField>`.
* `<FieldError>` doesn't render (i.e. returns `null`) when there's no error on `<TextField>`.
*
* ```jsx
* <Label name="name" errorClassName="error">
Expand Down Expand Up @@ -1002,6 +1012,7 @@ export {
Form,
ServerErrorsContext,
FormError,
RWGqlError,
FieldError,
InputField,
Label,
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6206,7 +6206,7 @@ __metadata:
pascalcase: 1.0.0
react: 17.0.2
react-dom: 17.0.2
react-hook-form: 7.33.0
react-hook-form: 7.33.1
typescript: 4.7.3
peerDependencies:
graphql: 16.5.0
Expand Down Expand Up @@ -25818,12 +25818,12 @@ __metadata:
languageName: node
linkType: hard

"react-hook-form@npm:7.33.0":
version: 7.33.0
resolution: "react-hook-form@npm:7.33.0"
"react-hook-form@npm:7.33.1":
version: 7.33.1
resolution: "react-hook-form@npm:7.33.1"
peerDependencies:
react: ^16.8.0 || ^17 || ^18
checksum: 86555a461186e3c03116c215229fa8a7831f28e1238184f12b5fc840c429c56e2a2976be06f68506d30759438622ebbfc6498c91556e1633b1ae4d500b0bc3f1
checksum: 6380700d877604be4326c845599240a42bb4eb31109ff47cd6aa9e567a906c9fe512c965396a72c6a0e52a4992922226ea58cf8bfb43d6ebbaa08972e8be4c65
languageName: node
linkType: hard

Expand Down

0 comments on commit 9db899e

Please sign in to comment.