reactjs - react 高阶组件在 typescript 中不起作用

标签 reactjs typescript higher-order-components

问题

我正在尝试将 HOC 从 javascript 提升到 typescript。 HOC 在使用它的组件中添加了一个确认对话框,提供了一个 showConfirmationDialog 属性,它在被调用时显示对话框并在点击确认时运行回调。

代码编译正常,但是当我在浏览器中打开应用程序时,出现错误“无效的 Hook 调用。 Hook 只能在函数组件的主体内部调用。”

该代码在 javascript 中运行良好。我无法理解该错误,并且我已按照所有建议的步骤进行操作,但没有任何解决方法。

代码

ConfirmationDialog/index.tsx

type ExtraProps = {
    showConfirmationDialog: (params: RequiredParameters) => void
}

type ConfirmationCallback = () => void

interface RequiredParameters {
    dialogTitle: string,
    dialogContent: string,
    confirmationButtonText: string,
    onConfirm: ConfirmationCallback
}

const WithConfirmationDialog = <P extends ExtraProps>(Component: React.ComponentType<P>) => {

    const [open, setOpen] = useState(false)
    const [title, setTitle] = useState('')
    const [content, setContent] = useState('')
    const [confirmationButtonText, setConfirmationButtonText] = useState('')
    const [onConfirm, setOnConfirm] = useState<ConfirmationCallback>()

    const handleShow = (params: RequiredParameters) => {
        setTitle(params.dialogTitle)
        setContent(params.dialogContent)
        setConfirmationButtonText(params.confirmationButtonText)
        setOnConfirm(params.onConfirm)
        setOpen(true)
    }

    const handleConfirm = () => {
        if (onConfirm) {
            onConfirm()
        }
        setOpen(false)
    }

    const handleClose = () => {
        setOpen(false)
    }

    const ComponentWithConfirmationDialog = (props: P) => (
        <>
            <Dialog
                open={open}
                onClose={handleClose}
            >
                <DialogTitle>{title}</DialogTitle>
                <DialogContent>
                    <DialogContentText>{content} </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleConfirm} color="primary">
                        {confirmationButtonText}
                    </Button>
                    <Button onClick={handleClose} color="primary">
                        Cancel
                    </Button>
                </DialogActions>
            </Dialog>
            <Component {...props} showConfirmationDialog={handleShow} />
        </>
    )

    return ComponentWithConfirmationDialog
}

export default WithConfirmationDialog

单击另一个组件中的按钮后使用代码的示例:

import withConfirmationDialog from '../ConfirmationDialog'

const MyComponent = (props) => {
  const classes = useStyles();

  const handleDeleteBooking = () => {
    // ...make api calls and handle results...
  };

  // left out everything else for brevity

  return (
    <Fab // material-ui
      className={classes.deleteButton}
      aria-label="delete"
      onClick={(e) => {
        props.showConfirmationDialog({
          dialogTitle: "Delete Booking",
          dialogContent: "Are you sure you want to delete this booking?",
          confirmationButtonText: "Delete",
          onConfirm: handleDeleteBooking,
        });
      }}
    >
      <DeleteIcon /> // material-ui
    </Fab>
  );
};

export default withConfirmationDialog(MyComponent)

附加信息

我用来构建这个的主要指南可以是 found here . 运行 npm start 时编译正常,错误永远不会显示在终端中。 我只在我的浏览器中看到“无效 Hook 调用”消息,以及指向我第一次在我的 HOC 中使用 useState(false 的堆栈跟踪。

如有任何帮助,我们将不胜感激!

最佳答案

这里的问题是您的 HOC 正在函数组件 ComponentWithConfirmationDialog 之外调用 Hook 。所有钩子(Hook)都必须在组件内部调用,而不是外部。您的 HOC 函数本身不是组件。

为了解决这个问题,您需要将 ComponentWithConfirmationDialog 上方的所有内容移动到其中,例如:

const WithConfirmationDialog = <P extends ExtraProps>(Component: React.ComponentType<P>) => {

  const ComponentWithConfirmationDialog = (props: P) => {
    const [open, setOpen] = useState(false)
    const [title, setTitle] = useState('')
    const [content, setContent] = useState('')
    const [confirmationButtonText, setConfirmationButtonText] = useState('')
    const [onConfirm, setOnConfirm] = useState<ConfirmationCallback>()

    const handleShow = (params: RequiredParameters) => {
        setTitle(params.dialogTitle)
        setContent(params.dialogContent)
        setConfirmationButtonText(params.confirmationButtonText)
        setOnConfirm(params.onConfirm)
        setOpen(true)
    }

    const handleConfirm = () => {
        if (onConfirm) {
            onConfirm()
        }
        setOpen(false)
    }

    const handleClose = () => {
        setOpen(false)
    }

    return (
        <>
            <Dialog
                open={open}
                onClose={handleClose}
            >
                <DialogTitle>{title}</DialogTitle>
                <DialogContent>
                    <DialogContentText>{content} </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleConfirm} color="primary">
                        {confirmationButtonText}
                    </Button>
                    <Button onClick={handleClose} color="primary">
                        Cancel
                    </Button>
                </DialogActions>
            </Dialog>
            <Component {...props} showConfirmationDialog={handleShow} />
        </>
    )
 }
    return ComponentWithConfirmationDialog
}

export default WithConfirmationDialog

关于reactjs - react 高阶组件在 typescript 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62392542/

相关文章:

javascript - componentWillUnmount 与 React useEffect Hook

typescript - Readonly<[]> 和 ReadOnlyArray<> 之间的区别

javascript - 修改从空白已呈现可见的网页复制文本的方式

reactjs - 如何使用高阶组件访问状态

javascript - 组件是否从称为闭包的高阶组件返回?

javascript - 将圆形边框转换为可点击功能

javascript - 类型错误 : Cannot read property 'current' of undefined focus

javascript - 特定组件的 PropTypes?

angular - NgRx 效果 mergeMap 方法

javascript - 我可以使用 HOC 用 graphql 和 redux 包装组件吗