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

时间:2020-10-30 15:39:02

标签: reactjs typescript ionic-framework react-hooks use-state

我正在尝试向我的应用添加IonPopover。最初,我是通过定义自定义元素并使用popoverController创建弹出框来实现的。它工作正常,但感觉有点混乱,因此我将组件从类转换为功能组件以使用React钩子。 enter image description here显示以下示例代码:

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>
    </>
  );
};

现在,这主要是我正在使用的内容。该文档还说:

为了相对于单击的元素定位弹出框,需要将click事件传递到present方法的选项中。如果未传递事件,则弹出框将位于视口的中心。 我不希望这个弹出窗口位于中心,因此我需要通过一个事件。这是我遇到麻烦的地方。 我这样创建了一个钩子:

const [popoverEvent, setPopoverEvent] = useState();

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

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

然后我将其传递给弹出窗口:

<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或类似的内容。

如何将该事件传递给弹出窗口,使其显示在按钮上?

这是完整的代码:

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;

2 个答案:

答案 0 :(得分:0)

当我这样做时,它将在我的项目中运行。您的代码不同吗?

BluetoothDeviceItem.js

paginationKey

Popover.js

selenium

答案 1 :(得分:0)

这是一个打字稿错误。如果调用export interface BookList { title: string; price: number; cover: string synopsis: string; } ,则打字稿会根据初始值推断状态的类型。但是,当您调用没有初始值的useState(someInitialValue)时,打字稿实际上会确定此状态的值为useState(),并且永远只能是undefined。尝试undefined以外的其他任何操作时,您会得到一个错误,这就是这里发生的情况。

由于打字稿无法推断状态类型,因此您必须通过手动在setState上设置通用变量来告诉打字稿期待什么。

undefined

现在已知useState的类型是const [popoverEvent, setPopoverEvent] = useState<MouseEvent<HTMLIonIconElement, MouseEvent>>(); popoverEvent,并且在事件中调用MouseEvent<HTMLIonIconElement, MouseEvent>时不会再有错误。