Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref not working in React-Bootstrap + TypeScript #598

Closed
CKGrafico opened this issue Nov 29, 2019 · 21 comments
Closed

ref not working in React-Bootstrap + TypeScript #598

CKGrafico opened this issue Nov 29, 2019 · 21 comments
Labels
question Further information is requested

Comments

@CKGrafico
Copy link

CKGrafico commented Nov 29, 2019

Describe the bug
If I use ref with register in a Control the value is invalid.

image

No overload matches this call.
  Overload 1 of 2, '(props: Readonly<ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>>): FormControl<"input">', gave the following error.
    Type '(ref: ElementLike) => void' is not assignable to type '(string & ((instance: HTMLInputElement) => void)) | (string & RefObject<HTMLInputElement>) | (((instance: FormControl<"input">) => void) & ((instance: HTMLInputElement) => void)) | (((instance: FormControl<...>) => void) & RefObject<...>) | (RefObject<...> & ((instance: HTMLInputElement) => void)) | (RefObject<...> ...'.
      Type '(ref: ElementLike) => void' is not assignable to type 'string & ((instance: HTMLInputElement) => void)'.
        Type '(ref: ElementLike) => void' is not assignable to type 'string'.
  Overload 2 of 2, '(props: ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>, context?: any): FormControl<"input">', gave the following error.
    Type '(ref: ElementLike) => void' is not assignable to type '(string & ((instance: HTMLInputElement) => void)) | (string & RefObject<HTMLInputElement>) | (((instance: FormControl<"input">) => void) & ((instance: HTMLInputElement) => void)) | (((instance: FormControl<...>) => void) & RefObject<...>) | (RefObject<...> & ((instance: HTMLInputElement) => void)) | (RefObject<...> ...'.
      Type '(ref: ElementLike) => void' is not assignable to type 'string & ((instance: HTMLInputElement) => void)'.
        Type '(ref: ElementLike) => void' is not assignable to type 'string'.ts(2769)
index.d.ts(104, 9): The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<FormControl<"input">> & Readonly<ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>> & Readonly<{ children?: ReactNode; }>'
index.d.ts(104, 9): The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<FormControl<"input">> & Readonly<ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>> & Readonly<{ children?: ReactNode; }>'

if instead of register() I use () => register() the code compiles but is not registering the input.

Screenshots
image

@bluebill1049 bluebill1049 added the question Further information is requested label Nov 29, 2019
@bluebill1049
Copy link
Member

bluebill1049 commented Nov 29, 2019

This is not a bug. for external controlled component:

https://react-hook-form.com/get-started/#ControlledInput

1). custom register, register input during useEffect
2). react-hook-form-input https://github.com/react-hook-form/react-hook-form-input to wrap around your controlled component

@CKGrafico
Copy link
Author

thank you so much

@bluebill1049
Copy link
Member

you are welcome

@CKGrafico
Copy link
Author

To add more info for new people, my code now looks like this.
image

@bluebill1049
Copy link
Member

bluebill1049 commented Nov 29, 2019

thanks @CKGrafico or you can register during useEffect as well

const { register, setValue } = useForm();
useEffect(() => {
  register({ name: 'email' })
}, [register])

<Control type="email" onChange={(e) => setValue('email', e.target.value)} />

@CKGrafico
Copy link
Author

CKGrafico commented Nov 29, 2019

Yes, I've read both approaches but the first seems cleaner, do you recommend the second one @bluebill1049?

@bluebill1049
Copy link
Member

I am happy with either :) but it's good to know what's behind the component, which is basically a custom register 👍

@mnesarco
Copy link

Hi Guys, I have a similar issue, ref works with react-bootstrap if used like this:

ref={register}

But if used with any option it does not and generates the same error:

ref={register({required:true})}

I understand that the suggested workaround (react-hook-form-input) works, but I lose much of the clarity and elegance provided by react-hook-form. I wonder why register works without options but not with options. Maybe some clarifications about the difference between register and register({...}) can help.

@bluebill1049
Copy link
Member

@mnesarco can you supply a codesandbox for this? i can take a look why.

@mnesarco
Copy link

Thank you @bluebill1049 for putting time into this, right now I am finishing a product and have no time to supply the codesandbox example this week. by the way, I used the component and it worked very well without adding to much verbosity.

            <Form.Group controlId="loginEmail">
                <Form.Label>Email address</Form.Label>
                <Controller
                    as={
                        <Form.Control
                            type="email"
                            placeholder="Enter email"
                            isInvalid={!!errors.user} />
                    }
                    control={control}
                    name="user"
                    rules={{ required: true }}
                />
                {errors.user &&
                    <Form.Control.Feedback type="invalid">
                        {errors.user.message || 'Required'}
                    </Form.Control.Feedback>
                }
            </Form.Group>


I will try to come back to this next week and provide the codesandbox.

Thank you for this great lib.

@korzo
Copy link

korzo commented Jan 7, 2020

@bluebill1049 Here is condesandobox for issue @mnesarco mentioned.

https://codesandbox.io/s/quirky-fire-nxyo6

Using ref={register} works, but ref={register({ required: true })} shows this error

Error:(36, 21) TS2769: No overload matches this call.
  Overload 1 of 2, '(props: Readonly<ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>>): FormControl<"input">', gave the following error.
    Type '(ref: ElementLike | null) => void' is not assignable to type '(string & ((instance: HTMLInputElement | null) => void)) | (string & RefObject<HTMLInputElement>) | (((instance: FormControl<"input"> | null) => void) & ((instance: HTMLInputElement | null) => void)) | ... 4 more ... | undefined'.
      Type '(ref: ElementLike | null) => void' is not assignable to type 'string & ((instance: HTMLInputElement | null) => void)'.
        Type '(ref: ElementLike | null) => void' is not assignable to type 'string'.
  Overload 2 of 2, '(props: ReplaceProps<"input", BsPrefixProps<"input"> & FormControlProps>, context?: any): FormControl<"input">', gave the following error.
    Type '(ref: ElementLike | null) => void' is not assignable to type '(string & ((instance: HTMLInputElement | null) => void)) | (string & RefObject<HTMLInputElement>) | (((instance: FormControl<"input"> | null) => void) & ((instance: HTMLInputElement | null) => void)) | ... 4 more ... | undefined'.
      Type '(ref: ElementLike | null) => void' is not assignable to type 'string & ((instance: HTMLInputElement | null) => void)'.
        Type '(ref: ElementLike | null) => void' is not assignable to type 'string'.

@bluebill1049
Copy link
Member

Doesn’t looks like the error is coming from RHF. Maybe try to use controller?

@frankie567
Copy link

From what I've tried, it seems that it's only a mismatch between how RHF and React-Bootstrap type a ref. It works perfectly by doing a simple cast:

type RBRef = (string & ((ref: Element | null) => void));

<Form.Control type="text" ref={register({ required: true }) as RBRef}></Form.Control>

@bluebill1049
Copy link
Member

Thanks for sharing @frankie567

@mtford90
Copy link

The problem seems to be here:

register<Element extends FieldElement = FieldElement>(refOrValidationOptions: ValidationOptions | Element | null, validationOptions?: ValidationOptions): ((ref: Element | null) => void) | void;

The return type of register is either (ref: Element | null) => void or void - and the ref property does not like void

@frankie567
Copy link

This seems fixed with react-bootstrap@^1.0.0 (at least). The trick I mentioned above is not needed anymore, so I guess we can move on now 🙂

@CBurrows87
Copy link

Just for some clarity for myself with using React bootstrap with RHF

<Form.Control as="select" name={"Title"} ref={register({ required: true })} > <option value="Mr">Mr</option> <option value="Miss">Miss</option> <option value="Mrs">Mrs</option> </Form.Control>
Will this work as RHF intends as i have registered it via the ref, or will i need to wrap it in the tag regardless? (Or register it in UseEffect?)

thanks!

@bluebill1049
Copy link
Member

Just for some clarity for myself with using React bootstrap with RHF

<Form.Control as="select" name={"Title"} ref={register({ required: true })} > <option value="Mr">Mr</option> <option value="Miss">Miss</option> <option value="Mrs">Mrs</option> </Form.Control>
Will this work as RHF intends as i have registered it via the ref, or will i need to wrap it in the tag regardless? (Or register it in UseEffect?)

thanks!

if that works: yes correct usage which means Form.Control expose ref correctly. otherwise at useEffect or using Controller

@SalahAdDin
Copy link

I have the same problem with Ionic React.

@bluebill1049
Copy link
Member

I have the same problem with Ionic React.

watch this? https://www.youtube.com/watch?v=5MsXpmh3Un8

@rodnierbc
Copy link

rodnierbc commented Nov 24, 2020

// Here a full example

import React, { FC, useEffect, useState } from 'react';
import {Row, Col, Card, Form, Button, InputGroup, FormControl} from 'react-bootstrap';

import { useForm } from "react-hook-form";
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';

const AddUser: FC = ({ history, match }: any) => {
   // form validation rules 
    const validationSchema = Yup.object().shape({
        email: Yup.string()
            .email('Email is invalid')
            .required('Email is required'),
    });
   // functions to build form returned by useForm() hook
    const { register, handleSubmit, reset, setValue, getValues, errors, formState } = useForm({
        resolver: yupResolver(validationSchema)
    });
   
   return(
            <Card>
                <Card.Body>
                <h5>{ 'Create User'}</h5>
                <hr/>
                    <Row>
                        <Col>
                            <Form onSubmit={handleSubmit(onSubmit)} onReset={reset}>
                                <Form.Row>
                                    <Form.Group as={Col} controlId="formGridEmail">
                                        <Form.Label>Email</Form.Label>
                                        <Form.Control 
                                             type="text" 
                                             name="email" 
                                             ref={register({ required: true })} 
                                             className={`form-control ${errors.email ? 'is-invalid' : ''}`} 
                                         />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.email?.message}
                                        </Form.Control.Feedback>
                                    </Form.Group>
                                </Form.Row>
                                <Form.Group as={Col} controlId="formControls">
                                    <Button 
                                        type="submit" 
                                        disabled={formState.isSubmitting} 
                                         className="btn btn-primary">
                                         {formState.isSubmitting && <span className="spinner-border spinner-border-sm mr-1"> 
                                         </span>}
                                         Save
                                    </Button>
                                </Form.Group>
                            </Form>
                        </Col>
                    </Row>
                </Card.Body>
            </Card>
    );
}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

9 participants