6

I am using [email protected] and I am trying to create a custom Button component using react-bootstraps Button, adding another prop, isLoading. Because of the way react-bootstrap types are defined I ended up importing some helpers types and copying a part of react-bootstrap types:

import React from 'react'
import { Button as Btn, ButtonProps } from 'react-bootstrap'
import { BsPrefixProps, ReplaceProps } from 'react-bootstrap/helpers'

interface Props {
  isLoading?: boolean
}

type BtnProps<As extends React.ElementType = 'button'> = ReplaceProps<
  As,
  BsPrefixProps<As> & ButtonProps
>

export const Button: React.FC<BtnProps & Props> = ({ isLoading, disabled, ...props }) => {
  return <Btn {...props} disabled={isLoading || disabled} />
}

This almost works. The error I got is telling me that ref prop types are wrong:

Types of property 'ref' are incompatible.

...

Type '(instance: HTMLButtonElement | null) => void' is not assignable to type '(instance: Button> | null) => void'.

I stripped most of the error message to get the relevant bit. Do I need to wrap the component in forwardRef for this work?

4 Answers 4

4

you can see better way of doing is at following code piece.

import React from 'react';
import { Button, ButtonProps } from 'react-bootstrap'

interface PropType extends ButtonProps {
    buttonText: string;
    isSubmitting: boolean;
}

export const SubmitButton: React.FC<PropType> = (props: PropType) => {
let { isSubmitting, buttonText, ...remainingProps } = props;
    return (
        <Button  variant="primary" type="submit" disabled={props.isSubmitting} style={{margin: 10}} {...remainingProps}>
            {props.buttonText}
        </Button>
    )
}
Sign up to request clarification or add additional context in comments.

1 Comment

this won't work for the same reason this isn't working stackoverflow.com/a/60324501/2874705
1

So, I decided to solve this with a workaround:

export const Button: React.FC<Props & React.ButtonHTMLAttributes<HTMLButtonElement>> = 
({ isLoading, disabled, ...props }) => 
<Btn disabled={isLoading || props.disabled} {...props} />

This is not ideal because in theory, Btn can be a different native element (not always a button) but it's good enough for my use case.

1 Comment

I struggled with this exact problem for hours, without a satisfying result. Looks like your approach is the only working one, thanks for sharing.
1

I know this was answered a while back, but I had a similar problem and am fairly happy I just about got to the 'right' solution. In detail it's here: https://shaderfun.com/2021/10/04/extending-boot-strap-in-type-script/

This example shows how I did it:

//imports
import React from "react";
import { Button, ButtonProps } from "react-bootstrap";
import { BsPrefixProps, ReplaceProps } from "react-bootstrap/helpers"
import { BsInfo } from 'react-icons/bs'

//handy type defintion to wrap up the replace+bsprefix bits of bootstrap
type BootstrapComponentProps<As extends React.ElementType, P> = ReplaceProps<As, BsPrefixProps<As> & P>

//our extended button properties
type NewButtonProps = {
    //any new properties we want to add go here
} & ButtonProps

//boot-strap-ified full button properties with all the bells and whistles
type MyInfoButtonProperties<As extends React.ElementType = 'button'> =
    BootstrapComponentProps<As, NewButtonProps>

//our button!
export function MyInfoButton<As extends React.ElementType = 'button'>(props: MyInfoButtonProperties<As>) {
    return <Button {...props} 
        variant={props.variant || "light"}>
        <BsInfo/>
    </Button>
}

I was aiming to create a button that defaulted to 'light' and contained the BsInfo icon from react-icons.

Comments

0

I am just wondering if the following simplification would not suffice?

import React from 'react'
import { Button as Btn, ButtonProps } from 'react-bootstrap'

interface Props {
  isLoading?: boolean
}

export const Button: React.FC<ButtonProps & Props> = ({ isLoading, ...props }) => {
  return <Btn {...props} disabled={isLoading || props.disabled} />
}

1 Comment

not really because ButtonProps are missing all the props you would want to pass to native button element, like className, onClick etc

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.