【问题标题】:React: Function components cannot be given refs when displaying a modalReact:显示模式时不能给函数组件提供参考
【发布时间】:2019-11-27 13:59:02
【问题描述】:

模态框在 App.js 中的 hashRouter 下渲染。模态在其状态 isOpen 为 true 时显示。

我在尝试使用 react redux 显示 Modal 时遇到此错误。

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `TrapFocus`.
    in ConnectFunction (at Modal.js:41)
    in TrapFocus (created by ForwardRef(Modal))
    in div (created by ForwardRef(Modal))
    in ForwardRef(Portal) (created by ForwardRef(Modal))
    in ForwardRef(Modal) (at Modal.js:35)
    in Modal (created by ConnectFunction)
    in ConnectFunction (created by WithStyles(undefined))
    in WithStyles(undefined) (at App.js:46)
    in ThemeProvider (at App.js:24)
    in App (created by WithStyles(App))
    in WithStyles(App) (at src/index.js:13)
    in Provider (at src/index.js:12)

这是我的代码:

Modal.js

import React, { Component } from "react";
import { compose } from "redux";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Modal as MaterialModal } from "@material-ui/core";
import { withStyles } from "@material-ui/styles";
import { closeModal } from "../store/actions/actions-ui";
import BasicModal from "./Modals/BasicModal";

const ModalTypes = {
    BasicModal,
};

const styles = {
    modal: {
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
    },
};

class Modal extends Component {
    handleCloseModal = () => {
        const { dispatch, shouldCloseOnBackgroundTouch } = this.props;
        if (shouldCloseOnBackgroundTouch) dispatch(closeModal());
    };

    render() {
        const { modalType, data, isOpen, classes } = this.props;
        if (!modalType) {
            return null;
        }
        const ModalToRender = ModalTypes[modalType];
        return (
            <MaterialModal
                disableAutoFocus
                className={classes.modal}
                open={isOpen}
                onClose={this.handleCloseModal}
            >
                <ModalToRender {...data} />
            </MaterialModal>
        );
    }
}

Modal.propTypes = {
    dispatch: PropTypes.func.isRequired,
    isOpen: PropTypes.bool.isRequired,
    modalType: PropTypes.string,
    // eslint-disable-next-line react/forbid-prop-types
    data: PropTypes.object,
    shouldCloseOnBackgroundTouch: PropTypes.bool.isRequired,
    classes: PropTypes.objectOf(PropTypes.string).isRequired,
};

Modal.defaultProps = {
    data: {},
    modalType: "",
};

const mapStateToProps = (state) => ({
    isOpen: state.ui.modalState.isOpen,
    shouldCloseOnBackgroundTouch: state.ui.modalState.shouldCloseOnBackgroundTouch,
    modalType: state.ui.modalState.modalType,
    data: state.ui.modalState.data,
});

export default compose(
    withStyles(styles),
    connect(mapStateToProps)
)(Modal);

BasicModal.js

import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import If from "../__helpers__/If";
import { closeModal } from "../../store/actions/actions-ui";

class BasicModal extends Component {
    handleClose = () => {
        const { dispatch } = this.props;
        dispatch(closeModal());
    };

    render() {
        const { title, text, extraBtnText, extraBtnAction } = this.props;
        return (
            <Paper>
                <If truthy={title}>
                    <Typography gutterBottom variant="h4">
                        {title}
                    </Typography>
                </If>
                <If truthy={text}>
                    <Typography gutterBottom vairant="body2">
                        {text}
                    </Typography>
                </If>

                <Button onClick={this.handleClose}>Close</Button>
                <If truthy={extraBtnAction && extraBtnText}>
                    <Button
                        onClick={() => {
                            extraBtnAction();
                            this.handleClose();
                        }}
                    >
                        {extraBtnText}
                    </Button>
                </If>
            </Paper>
        );
    }
}

BasicModal.propTypes = {
    dispatch: PropTypes.func.isRequired,
    title: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    extraBtnText: PropTypes.string,
    extraBtnAction: PropTypes.func,
};

export default connect()(BasicModal);

我猜问题在于我试图渲染这样的组件:

<ModalToRender {...data} />

但我似乎无法理解它有什么问题。

感谢任何帮助!

【问题讨论】:

标签: reactjs redux react-redux material-ui


【解决方案1】:

您需要为 Material-UI 的 Modal 包装的组件(例如 BasicModal)使用 forwardRef option in connect()

例子:

export default connect(null, null, null, {forwardRef: true})(BasicModal);

Refs 提供对 React 元素的 DOM 节点的访问。 Material-UI 的 TrapFocus(由 Modal 使用)uses a ref on the child 传递给 Modal 以管理焦点方面。

在您的情况下,传递给Modal 的子组件是connect()(BasicModal) 返回的包装组件。默认情况下,该包装器组件是一个函数组件,并且函数组件不能接受引用,除非被forwardRef 包装。

connectforwardRef 选项使其使用 React.forwardRef 包装函数组件,以便它可以成功接受传递给被包装组件的引用(在您的情况下为 BasicModal)。

【讨论】:

  • 解决了,谢谢!想解释一下 forwardRef 的用途吗?
  • 我已经添加了一些解释。
猜你喜欢
  • 1970-01-01
  • 2019-11-06
  • 2020-10-20
  • 1970-01-01
  • 2019-11-03
  • 2021-06-05
  • 1970-01-01
  • 1970-01-01
  • 2022-07-21
相关资源
最近更新 更多