使用鼠标事件作为值来反应useState钩子



我正在尝试将IonPopover添加到我的应用程序中。最初,我通过定义一个自定义元素,并使用popoverController创建一个popover来实现这一点。它工作得很好,但感觉有点乱,所以我将我的组件从一个类转换为一个函数组件,以使用React钩子。IonPopover文档显示了以下示例代码:

import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';
export const PopoverExample: React.FC = () => {
const [showPopover, setShowPopover] = useState(false);
return (
<>
<IonPopover
isOpen={showPopover}
cssClass='my-custom-class'
onDidDismiss={e => setShowPopover(false)}
>
<p>This is popover content</p>
</IonPopover>
<IonButton onClick={() => setShowPopover(true)}>Show Popover</IonButton>
</>
);
};

现在,这主要是我正在使用的。文件还说:

为了将popover相对于单击的元素定位,需要将单击事件传递到present方法的选项中。如果事件未通过,则弹出窗口将位于视口的中心。我不想让这个popover在中心,所以我需要通过一个活动。这就是我遇到麻烦的地方。我创建了这样一个钩子:

const [popoverEvent, setPopoverEvent] = useState();

当按下打开弹出窗口的按钮时,将运行此代码,将popoverEvent的值设置为事件。

onClick={(e) => { setShowPopover(true); setPopoverEvent(e); }}

然后我把它传给popover:

<IonPopover
isOpen={showPopover}
cssClass='popover-style-class'
event={popoverEvent}
onDidDismiss={_ => { setShowPopover(false); setPopoverEvent(null); }}
>
//.....

然而,这种方式会产生一个很大的打字错误:

Argument of type 'MouseEvent<HTMLIonIconElement, MouseEvent>' is not assignable to parameter of type '(prevState: undefined) => undefined'.
Type 'MouseEvent<HTMLIonIconElement, MouseEvent>' provides no match for the signature '(prevState: undefined): undefined'.

无论我将useState设置为什么(即undefinedMouseEventEvent(,我都会在文件中的某个位置出现错误,因为我不喜欢将类型x分配给类型y或类似的内容

如何将此事件传递给popover,使其显示在按钮上?

这是完整的代码:

const BluetoothDeviceItem = (Props, { }) => {
const [icon, setIcon] = useState(closeOutline);
const [showPopover, setShowPopover] = useState(false);
const [popoverEvent, setPopoverEvent] = useState();
useEffect(() => {
updateIcons();
}, []);
const updateIcons = () => {
const item = $("#" + Props.address);
const connectingIcon = $(item).find(".connecting-icons");
const spinnerIcon = $(item).find(".connecting-spinner");
switch (Props.state()) {
case 'not_connected':
spinnerIcon.css("opacity", "0");
connectingIcon.css("color", "var(--ion-color-danger)");
connectingIcon.css("opacity", "1");
setIcon(closeOutline);
break;
case 'connecting':
spinnerIcon.css("opacity", "1");
connectingIcon.css("opacity", "0");
setIcon("");
break;
case 'connected':
spinnerIcon.css("opacity", "0");
connectingIcon.css("opacity", "1");
connectingIcon.css("color", "var(--ion-color-success)");
setIcon(checkmarkOutline);
break;
}
}
const rerender = () => {
updateIcons();
}
const handleClick = async () => {
const interactable = Props.interactable || true;
if (Props.state() !== "connecting" && Props.onClick && interactable) Props.onClick(Props.address); //If the device is already connecting, don't allow user to click again
rerender();
}
const toggleChanged = () => {
console.log("here")
}
const interactable = Props.interactable || true;
return (
<IonItem class={Props.class || "" + (Props.interactable ? " bluetooth-item-container" : "")} id={Props.address} onClick={handleClick}>
<IonAvatar slot="start" class="bluetooth-icon-container">
<IonIcon icon={bluetoothOutline} class="bluetooth-icon"></IonIcon>
</IonAvatar>
<IonAvatar slot="end" class="connecting-icons-container">
<IonIcon icon={icon} class="connecting-icons"></IonIcon>
<IonSpinner name="dots" class="connecting-spinner"></IonSpinner>
{interactable ? <IonIcon icon={chevronDownOutline} class="popover-icon" onClick={(e) => { setShowPopover(true); setPopoverEvent(e); }}></IonIcon> : <></>}
</IonAvatar>
<IonLabel>
<div className="device-name">{Props.name || "Unknown"}</div>
<div className="device-address">{Props.address}</div>
</IonLabel>
<IonPopover
isOpen={showPopover}
cssClass='popover-style-class'
event={popoverEvent}
onDidDismiss={_ => { setShowPopover(false); setPopoverEvent(null); }}
>
<IonList>
<IonItem lines="none" class="popover-item">
<IonLabel class="popover-autoconnect-label">Autoconnect</IonLabel>
<IonToggle slot="end" color="success" onIonChange={toggleChanged}></IonToggle>
</IonItem>
</IonList>
</IonPopover>
</IonItem>
);
}
export default BluetoothDeviceItem;

这是一个类型脚本错误。如果调用useState(someInitialValue),typescript将从初始值推断状态的类型。但当您在没有初始值的情况下调用useState()时,typescript实际上确定该状态的值是undefined,并且只能是undefined。当您尝试将setState转换为undefined以外的任何内容时,都会收到一个错误,这就是这里发生的情况。

由于typescript无法推断状态类型,因此您必须通过手动设置useState上的泛型变量来告诉typescript所期望的内容。

const [popoverEvent, setPopoverEvent] = useState<MouseEvent<HTMLIonIconElement, MouseEvent>>();

现在popoverEvent的类型已知为MouseEvent<HTMLIonIconElement, MouseEvent>undefined,并且在使用事件调用setPopoverEvent时不会有任何错误。

当我这样做时,它在我的项目中起作用。你的代码不同吗?

蓝牙设备项.js

import React, { useState, useEffect } from 'react';
import { IonIcon, IonItem, IonAvatar, IonSpinner, IonLabel, IonList, IonToggle } from '@ionic/react';
import $ from "jquery";
//import { Icon, InlineIcon } from '@iconify/react';
import {checkmarkOutline, closeOutline, bluetoothOutline, chevronDownOutline} from '@iconify/icons-ion/checkmark-outline';
import {PopoverExample} from './Popover';
const BluetoothDeviceItem = (Props, {}) => {
const [icon, setIcon] = useState(closeOutline);
const [showPopover, setShowPopover] = useState(false);
const [popoverEvent, setPopoverEvent] = useState();
useEffect(() => {
updateIcons();
}, []);
const updateIcons = () => {
const item = $("#" + Props.address);
const connectingIcon = $(item).find(".connecting-icons");
const spinnerIcon = $(item).find(".connecting-spinner");
switch (Props.state) {
case 'not_connected':
spinnerIcon.css("opacity", "0");
connectingIcon.css("color", "var(--ion-color-danger)");
connectingIcon.css("opacity", "1");
setIcon(closeOutline);
break;
case 'connecting':
spinnerIcon.css("opacity", "1");
connectingIcon.css("opacity", "0");
setIcon("");
break;
case 'connected':
spinnerIcon.css("opacity", "0");
connectingIcon.css("opacity", "1");
connectingIcon.css("color", "var(--ion-color-success)");
setIcon(checkmarkOutline);
break;
}
}
const rerender = () => {
updateIcons();
}
const handleClick = async () => {
const interactable = Props.interactable || true;
if (Props.state !== "connecting" && Props.onClick && interactable) Props.onClick(Props.address); //If the device is already connecting, don't allow user to click again
rerender();
}
const toggleChanged = () => {
}
const interactable = Props.interactable || true;
return (
<IonItem class={Props.class || "" + (Props.interactable ? " bluetooth-item-container" : "")} id={Props.address} onClick={handleClick}>
<IonAvatar slot="start" class="bluetooth-icon-container">
<IonIcon icon={bluetoothOutline} class="bluetooth-icon"></IonIcon>
</IonAvatar>
<IonAvatar slot="end" class="connecting-icons-container">
<IonIcon icon={icon} class="connecting-icons"></IonIcon>
<IonSpinner name="dots" class="connecting-spinner"></IonSpinner>
{interactable ? <IonIcon icon={chevronDownOutline} class="popover-icon" onClick={(e) => { setShowPopover(true); setPopoverEvent(e); }}></IonIcon> : <></>}
</IonAvatar>
<IonLabel>
<div className="device-name">{Props.name || "Unknown"}</div>
<div className="device-address">{Props.address}</div>
</IonLabel>
<PopoverExample
isOpen={showPopover}
cssClass='popover-style-class'
event={popoverEvent}
onDidDismiss={_ => { setShowPopover(false); setPopoverEvent(null); }}
>
<IonList>
<IonItem lines="none" class="popover-item">
<IonLabel class="popover-autoconnect-label">Autoconnect</IonLabel>
<IonToggle slot="end" color="success" onIonChange={toggleChanged}></IonToggle>
</IonItem>
</IonList>
</PopoverExample>
</IonItem>
);
}
export default BluetoothDeviceItem;

Popover.js

import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';
export const PopoverExample: React.FC = () => {
const [showPopover, setShowPopover] = useState(false);
const [popoverEvent, setPopoverEvent] = useState();

return (
<>
<IonPopover
isOpen={showPopover}
cssClass='my-custom-class'
event={popoverEvent}
onDidDismiss={_ => { setShowPopover(false); setPopoverEvent(null); console.log("laskjdflskjf")}}
>
<p>This is popover content</p>
</IonPopover>
<IonButton onClick={(e) => { setShowPopover(true); setPopoverEvent(e); }}>Show Popover</IonButton>
</>
);
};

最新更新