aranlucas / react-hook-form-mantine

React Hook Form bindings for Mantine components
https://aranlucas.github.io/react-hook-form-mantine/
83 stars 12 forks source link

Usage with Tooltip (forwardRef) #14

Closed BrendanC23 closed 5 months ago

BrendanC23 commented 11 months ago

What is the suggested way to use the react-hook-form-mantine components with Mantine's Tooltip? If I do

<Tooltip label="Enter the SO number to find the BOL number.">
    <NumberInput
        name="number"
        control={form.control}
        label="Number"
    />
</Tooltip>

I get the error Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Mantine's Tooltip documentation says to use a ref prop (and that the default Mantine components have this built-in already).

I tried doing

const NumberInputWithRef = forwardRef<HTMLInputElement, NumberInputProps<FormType>>((props, ref) => (
    <NumberInput ref={ref} {...props} />
));

<Tooltip>
    <NumberInputWithRef ... />
</Tooltip>

but got the error Property 'ref' does not exist on type 'IntrinsicAttributes & UseControllerProps<...>.

Here's a StackBlitz of the issue. There is a console error for attempt 1 and a TypeScript error for attempt 2.

aranlucas commented 10 months ago

This is an interesting issue.

Both react-hook-form and mantine require ref, so it's being overwritten by the react-hook-form-ref.

One potential solution from my side would be switch the order of

      {...field}
      {...props}

and allow ref to come from the parent component. I'll need to think about this.

aranlucas commented 10 months ago

Found something related to this:

https://www.react-hook-form.com/faqs/#Howtosharerefusage

I'll need to figure how to share that ref usage

aranlucas commented 10 months ago

Here's where I'm at

Change components to use forwardRef. Taking your example of NumberInput

import { forwardRef } from "react";
import {
  type UseControllerProps,
  useController,
  type FieldValues,
} from "react-hook-form";
import {
  NumberInput as $NumberInput,
  type NumberInputProps as $NumberInputProps,
} from "@mantine/core";

export type NumberInputProps<T extends FieldValues> = UseControllerProps<T> &
  Omit<$NumberInputProps, "value" | "defaultValue">;

export const NumberInput = forwardRef(function NumberInput<
  T extends FieldValues,
>(
  {
    name,
    control,
    defaultValue,
    rules,
    shouldUnregister,
    onChange,
    ...props
  }: NumberInputProps<T>,
  ref: React.Ref<HTMLInputElement>,
) {
  const {
    field: { value, onChange: fieldOnChange, ref: insideRef, ...field },
    fieldState,
  } = useController<T>({
    name,
    control,
    defaultValue,
    rules,
    shouldUnregister,
  });

  return (
    <$NumberInput
      ref={mergedRef}
      value={value}
      onChange={(e) => {
        fieldOnChange(e);
        onChange?.(e);
      }}
      error={fieldState.error?.message}
      {...field}
      {...props}
    />
  );
});

This now breaks generics. In App.tsx,

    <NumberInput
      name="numberInput"
      // ts error here due to not forwarding generics
      control={control}
      placeholder="Your age"
      label="Your age"
      withAsterisk
    />

I looked into solutions such https://fettblog.eu/typescript-react-generic-forward-refs/, https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref. I settled on redeclaring forwardRef.

// Redecalare forwardRef
declare module "react" {
  function forwardRef<T, P = {}>(
    render: (props: P, ref: React.Ref<T>) => React.ReactNode | null,
  ): (props: P & React.RefAttributes<T>) => React.ReactNode | null;
}

The you need to find a way to merge refs using https://chakra-ui.com/docs/hooks/use-merge-refs or https://github.com/gregberge/react-merge-refs/tree/main/src

  const mergedRef = useMergeRefs(insideRef, ref);

  return (
    <$NumberInput
      ref={mergedRef}
      value={value}
      onChange={(e) => {
        fieldOnChange(e);
        onChange?.(e);
      }}
      error={fieldState.error?.message}
      {...field}
      {...props}
    />

now that you've re-declared react forward ref, all the react things aren't working well.

If you find a good way to solve these 2 issues, PR is welcome.

aranlucas commented 10 months ago

Here's a sample branch if you want this feature to be supported: https://github.com/aranlucas/react-hook-form-mantine/tree/forwardRef

Hopefully you find a better way to do make it compatible.

github-actions[bot] commented 8 months ago

Stale issue message

mpham-hai commented 7 months ago

can we reopen this?

github-actions[bot] commented 5 months ago

Stale issue message