I'm trying to implement the custom toggle drop-down example from the react-bootstrap page, in Typescript, using react functional components.
Here's my component code:
import React from 'react';
import Dropdown from 'react-bootstrap/Dropdown';
import FormControl from 'react-bootstrap/FormControl';
export const DropdownSelector =() => (
<Dropdown>
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components">
Custom toggle
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu}>
<Dropdown.Item eventKey="1">Red</Dropdown.Item>
<Dropdown.Item eventKey="2">Blue</Dropdown.Item>
<Dropdown.Item eventKey="3" active>
Orange
</Dropdown.Item>
<Dropdown.Item eventKey="1">Red-Orange</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)
// The forwardRef is important!!
// Dropdown needs access to the DOM node in order to position the Menu
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
▼
</a>
));
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
const CustomMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
const [value, setValue] = useState('');
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<FormControl
autoFocus
className="mx-3 my-2 w-auto"
placeholder="Type to filter..."
onChange={(e) => setValue(e.target.value)}
value={value}
/>
<ul className="list-unstyled">
{React.Children.toArray(children).filter(
(child) =>
!value || child.props.children.toLowerCase().startsWith(value),
)}
</ul>
</div>
);
},
);
This fails to compile:
./src/components/helpers/dropdown-selector.tsx
TypeScript error in ./src/components/helpers/dropdown-selector.tsx(25,52):
Property 'onClick' does not exist on type '{ children?: ReactNode; }'. TS2339
What am I doing wrong?
Stackblitz sandbox version here. Using that editor, I see a bunch of type errors (although it does run); but the IDE I'm using to develop the app won't let me run it with those errors...
I Think I did resolve your problem
import React, { useState } from "react";
import Dropdown from "react-bootstrap/Dropdown";
import FormControl from "react-bootstrap/FormControl";
interface IFruity {
id: number;
fruit: string;
prefix: string;
suffix?: string;
}
const fruits: IFruity[] = [
{ id: 1, fruit: "Apples", prefix: "How's about them " },
{ id: 2, fruit: "Pear", prefix: "A cracking ", suffix: "!" },
{ id: 3, fruit: "Oranges", prefix: "What rhymes with ", suffix: "?" },
{ id: 4, fruit: "Banana", prefix: "Fruit flies like a " },
{ id: 5, fruit: "Coconuts", prefix: "Oh what a lovely bunch of " },
{ id: 6, fruit: "Avocado", prefix: "Is an ", suffix: " even a fruit?" }
];
type CustomToggleProps = {
children?: React.ReactNode;
onClick?: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {};
};
// The forwardRef is important!!
// Dropdown needs access to the DOM node in order to position the Menu
const CustomToggle = React.forwardRef(
(props: CustomToggleProps, ref: React.Ref<HTMLAnchorElement>) => (
<a
href=""
ref={ref}
onClick={e => {
e.preventDefault();
props.onClick(e);
}}
>
{props.children}
<span style={{ paddingLeft: "5px" }}>▼</span>
</a>
)
);
type CustomMenuProps = {
children?: React.ReactNode;
style?: React.CSSProperties;
className?: string;
labeledBy?: string;
};
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
const CustomMenu = React.forwardRef(
(props: CustomMenuProps, ref: React.Ref<HTMLDivElement>) => {
const [value, setValue] = useState("");
return (
<div
ref={ref}
style={props.style}
className={props.className}
aria-labelledby={props.labeledBy}
>
<FormControl
autoFocus
className="mx-3 my-2 w-auto"
placeholder="Type to filter..."
onChange={e => setValue(e.target.value)}
value={value}
/>
<ul className="list-unstyled">
{React.Children.toArray(props.children).filter(
(child: any) =>
!value || child.props.children.toLowerCase().startsWith(value)
)}
</ul>
</div>
);
}
);
export const DropdownSelector = () => {
const [selectedFruit, setSelectedFruit] = useState(0);
const theChosenFruit = () => {
const chosenFruit: IFruity = fruits.find(f => f.id === selectedFruit);
return chosenFruit
? chosenFruit.prefix + chosenFruit.fruit + (chosenFruit.suffix || "")
: "Select a fruit";
};
return (
<Dropdown onSelect={(e: string) => setSelectedFruit(Number(e))}>
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components">
{theChosenFruit()}
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu}>
{fruits.map(fruit => {
return (
<Dropdown.Item key={fruit.id} eventKey={fruit.id.toString()}>
{fruit.fruit}
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
);
};
I'm facing the same issue when defining a custom dropdown with react-bootstrap,
What I did to workaround this problem was
const CustomToggle = React.forwardRef((props: any, ref) => (
const { children, onClick } = props
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With