React Typescript 使用 .bind 触发 onClick 按钮事件
React Typescript fire onClick button event using .bind
我有以下 Cart.tsx 代码,其中包含两个函数 onRemove
和 onAdd
以及 .bind()
传递一些数据:
const Cart = (props: CartProps) => {
// ...
const cartItemRemoveHandler = (id: string) => {
console.log("CartItemRemoveHandler");
};
const cartItemAddHandler = (item: CartItemProps) => {
console.log("CartItemAddHandler");
};
const cartItems = (
<ul className={classes["cart-items"]}>
{cartCtx.items.map((item) => (
<CartItem
key={item.id}
id={item.id}
name={item.name}
amount={item.amount}
price={item.price}
onRemove={cartItemRemoveHandler.bind(null, item.id)}
onAdd={cartItemAddHandler.bind(null, item)}
/>
))}
</ul>
);
// ...
};
CartItem.tsx:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: (id: string) => void;
onRemove?: (item: CartItemProps) => void;
}
const CartItem = (props: CartItemProps) => {
const price = `$${props.price.toFixed(2)}`;
return (
<li className={classes["cart-item"]}>
<div>
<h2>{props.name}</h2>
<div className={classes.summary}>
<span className={classes.price}>{price}</span>
<span className={classes.amount}>x {props.amount}</span>
</div>
</div>
<div className={classes.actions}>
<button onClick={props.onRemove}>-</button>
<button onClick={props.onAdd}>+</button>
</div>
</li>
);
};
错误发生在按钮中 onClick
功能的 CartItem.tsx 处。 onClick
为红色下划线,错误如下:
(JSX attribute) React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
Type '((id: string) => void) | undefined' is not assignable to type 'MouseEventHandler<HTMLButtonElement> | undefined'.
Type '((id: string) => void' is not assignable to type 'MouseEventHandler<HTMLButtonElement>'.
Types of parameters 'id' and 'event' are incompatible.
Type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to type 'string'
The expected type comes from property 'onClick' which is declared here on type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'
我不需要事件属性。我只想在单击按钮时执行该功能。我可以解决这个问题吗?感谢帮助。
更新:通过将接口 onAdd
和 onRemove
声明更改为:onAdd?: () => void;
和 onRemove?: () => void;
解决了错误。本质上,我只是从接口中的函数中删除了参数,结果如下:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: () => void;
onRemove?: () => void;
}
.bind()
函数正在处理参数。也没有必要在界面中定义它们,这是我的错误。
在onClick中使用箭头函数:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: (id: string) => void;
onRemove?: (item: CartItemProps) => void;
}
const CartItem = (props: CartItemProps) => {
const price = `$${props.price.toFixed(2)}`;
const item = {
id: props.id,
name: props.name,
amount: props.amount,
price: props.price,
} as CartItemProps;
return (
<li className={classes["cart-item"]}>
<div>
<h2>{props.name}</h2>
<div className={classes.summary}>
<span className={classes.price}>{price}</span>
<span className={classes.amount}>x {props.amount}</span>
</div>
</div>
<div className={classes.actions}>
<button onClick={() => props.onRemove?.(item)}>-</button>
<button onClick={() => props.onAdd?.(item.id)}>+</button>
</div>
</li>
);
};
关注错误链的这一行:
Type '((id: string) => void) | undefined' is not assignable to type 'MouseEventHandler<HTMLButtonElement> | undefined'.
此错误说明您配置的 onAdd
挂钩类型与接受 MouseEvent<...>
对象作为第一个参数的 onClick
处理程序不兼容。
但是,因为您将 id
和 item
绑定到 Cart
元素内,所以您实际上是将类型为 () => void
的函数向下传递给 CartItem
相反,这与您的接口声明不同。这些 () => void
函数与 onClick
处理程序兼容,因为它们会忽略传递给它们的任何参数。
因此,您可以通过将界面更新为以下内容来解决此问题:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: () => void;
onRemove?: () => void;
}
这样您就可以继续按原样使用以下部分:
// Cart
onRemove={cartItemRemoveHandler.bind(null, item.id)}
onAdd={cartItemAddHandler.bind(null, item)}
// CartItem
<button onClick={props.onRemove}>-</button>
<button onClick={props.onAdd}>+</button>
但是,如果将来 cartItemAddHandler
或 cartItemRemoveHandler
添加了更多参数,并且您没有正确绑定函数处理程序的所有参数,您将开始获得 MouseEvent<...>
对象意外传递给您的函数。
// Cart
cartItemRemoveHandler = (id: string, quantity: number) => { ... }
/* ... */
onRemove={cartItemRemoveHandler.bind(null, item)}
// CartItem
export interface CartItemProps
/* ... */
onRemove?: () => void;
/* ... */
}
/* ... */
<button onClick={props.onRemove}>-</button>
在运行时,当触发 onAdd
时,此处的 quantity
将被赋予 MouseEvent<...>
,而不是 number
。
您可以通过更新接口以接受 MouseEventHandler<HTMLButtonElement>
对象来防止此类错误,以便 TypeScript 在您未正确绑定处理程序时适当地报告错误。
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: MouseEventHandler<HTMLButtonElement>;
onRemove?: MouseEventHandler<HTMLButtonElement>;
}
此外,您可以交换以下行以防止 MouseEvent 传递到错误的位置:
cartItemRemoveHandler.bind(null, item)
用于:
() => cartItemRemoveHandler(item)
// or
(ev: MouseEvent<...>) => cartItemRemoveHandler(item) // no need to explicitly type the `ev` as a MouseEvent here, it will be inferred from the interface.
旁注:即使进行了这些更改,您用于 onAdd
的处理程序正在接受一个项目对象,但 onRemove
收到一个字符串。与您的原始界面相比,这是倒退的。
我有以下 Cart.tsx 代码,其中包含两个函数 onRemove
和 onAdd
以及 .bind()
传递一些数据:
const Cart = (props: CartProps) => {
// ...
const cartItemRemoveHandler = (id: string) => {
console.log("CartItemRemoveHandler");
};
const cartItemAddHandler = (item: CartItemProps) => {
console.log("CartItemAddHandler");
};
const cartItems = (
<ul className={classes["cart-items"]}>
{cartCtx.items.map((item) => (
<CartItem
key={item.id}
id={item.id}
name={item.name}
amount={item.amount}
price={item.price}
onRemove={cartItemRemoveHandler.bind(null, item.id)}
onAdd={cartItemAddHandler.bind(null, item)}
/>
))}
</ul>
);
// ...
};
CartItem.tsx:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: (id: string) => void;
onRemove?: (item: CartItemProps) => void;
}
const CartItem = (props: CartItemProps) => {
const price = `$${props.price.toFixed(2)}`;
return (
<li className={classes["cart-item"]}>
<div>
<h2>{props.name}</h2>
<div className={classes.summary}>
<span className={classes.price}>{price}</span>
<span className={classes.amount}>x {props.amount}</span>
</div>
</div>
<div className={classes.actions}>
<button onClick={props.onRemove}>-</button>
<button onClick={props.onAdd}>+</button>
</div>
</li>
);
};
错误发生在按钮中 onClick
功能的 CartItem.tsx 处。 onClick
为红色下划线,错误如下:
(JSX attribute) React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
Type '((id: string) => void) | undefined' is not assignable to type 'MouseEventHandler<HTMLButtonElement> | undefined'.
Type '((id: string) => void' is not assignable to type 'MouseEventHandler<HTMLButtonElement>'.
Types of parameters 'id' and 'event' are incompatible.
Type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to type 'string'
The expected type comes from property 'onClick' which is declared here on type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'
我不需要事件属性。我只想在单击按钮时执行该功能。我可以解决这个问题吗?感谢帮助。
更新:通过将接口 onAdd
和 onRemove
声明更改为:onAdd?: () => void;
和 onRemove?: () => void;
解决了错误。本质上,我只是从接口中的函数中删除了参数,结果如下:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: () => void;
onRemove?: () => void;
}
.bind()
函数正在处理参数。也没有必要在界面中定义它们,这是我的错误。
在onClick中使用箭头函数:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: (id: string) => void;
onRemove?: (item: CartItemProps) => void;
}
const CartItem = (props: CartItemProps) => {
const price = `$${props.price.toFixed(2)}`;
const item = {
id: props.id,
name: props.name,
amount: props.amount,
price: props.price,
} as CartItemProps;
return (
<li className={classes["cart-item"]}>
<div>
<h2>{props.name}</h2>
<div className={classes.summary}>
<span className={classes.price}>{price}</span>
<span className={classes.amount}>x {props.amount}</span>
</div>
</div>
<div className={classes.actions}>
<button onClick={() => props.onRemove?.(item)}>-</button>
<button onClick={() => props.onAdd?.(item.id)}>+</button>
</div>
</li>
);
};
关注错误链的这一行:
Type '((id: string) => void) | undefined' is not assignable to type 'MouseEventHandler<HTMLButtonElement> | undefined'.
此错误说明您配置的 onAdd
挂钩类型与接受 MouseEvent<...>
对象作为第一个参数的 onClick
处理程序不兼容。
但是,因为您将 id
和 item
绑定到 Cart
元素内,所以您实际上是将类型为 () => void
的函数向下传递给 CartItem
相反,这与您的接口声明不同。这些 () => void
函数与 onClick
处理程序兼容,因为它们会忽略传递给它们的任何参数。
因此,您可以通过将界面更新为以下内容来解决此问题:
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: () => void;
onRemove?: () => void;
}
这样您就可以继续按原样使用以下部分:
// Cart
onRemove={cartItemRemoveHandler.bind(null, item.id)}
onAdd={cartItemAddHandler.bind(null, item)}
// CartItem
<button onClick={props.onRemove}>-</button>
<button onClick={props.onAdd}>+</button>
但是,如果将来 cartItemAddHandler
或 cartItemRemoveHandler
添加了更多参数,并且您没有正确绑定函数处理程序的所有参数,您将开始获得 MouseEvent<...>
对象意外传递给您的函数。
// Cart
cartItemRemoveHandler = (id: string, quantity: number) => { ... }
/* ... */
onRemove={cartItemRemoveHandler.bind(null, item)}
// CartItem
export interface CartItemProps
/* ... */
onRemove?: () => void;
/* ... */
}
/* ... */
<button onClick={props.onRemove}>-</button>
在运行时,当触发 onAdd
时,此处的 quantity
将被赋予 MouseEvent<...>
,而不是 number
。
您可以通过更新接口以接受 MouseEventHandler<HTMLButtonElement>
对象来防止此类错误,以便 TypeScript 在您未正确绑定处理程序时适当地报告错误。
export interface CartItemProps {
id: string;
name: string;
amount: number;
price: number;
onAdd?: MouseEventHandler<HTMLButtonElement>;
onRemove?: MouseEventHandler<HTMLButtonElement>;
}
此外,您可以交换以下行以防止 MouseEvent 传递到错误的位置:
cartItemRemoveHandler.bind(null, item)
用于:
() => cartItemRemoveHandler(item)
// or
(ev: MouseEvent<...>) => cartItemRemoveHandler(item) // no need to explicitly type the `ev` as a MouseEvent here, it will be inferred from the interface.
旁注:即使进行了这些更改,您用于 onAdd
的处理程序正在接受一个项目对象,但 onRemove
收到一个字符串。与您的原始界面相比,这是倒退的。