反应:显示模态时不能给功能组件提供参考

时间:2019-07-18 15:16:39

标签: reactjs redux react-redux material-ui

模态在App.js中的hashRouter下呈现。当其状态为isOpen时,将显示模态。

在尝试显示带有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} />

但是我似乎不明白这是怎么回事。

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

您需要将forwardRef option in connect()用于由Material-UI的BasicModal包装的组件(例如Modal)。

示例:

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包装,否则不能接受引用。

forwardRef的{​​{1}}选项使它使用connect包装功能组件,以便它可以成功接受传递给包装组件的ref({{1} })。