为什么React对话框需要componentWillReceiveProps并在Stepper更改时再次弹出?

时间:2018-05-29 20:31:24

标签: reactjs material-design

我有一个使用Material UI的反应应用程序。我想要一个可以多次打开的对话框。并且,在“步进器”的一个步骤上使用的对话框。

下面的代码允许我多次打开对话框,但我的工作方式是添加componentWillReceiveProps,这个解决方案看起来很奇怪。如果没有componentWillReceiveProps,则会在第一次打开对话框,但不会在任何连续点击时打开。有没有更好的方法让它重置传入的道具?我假设我的问题是在第一次创建组件时设置了props,然后app不重新创建组件,而props使用close事件上设置的旧值?

此外,当我在步骤之间移动时,会有奇怪的行为。如果单击步骤2,则会弹出对话框。我如何重构以避免这种行为?

这些模式或多或少地来自这里的示例:https://material-ui.com/

import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import ReactDOM from 'react-dom'
import React, { Component } from 'react';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import StepContent from '@material-ui/core/StepContent';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Dialog from '@material-ui/core/Dialog';
import CloseIcon from '@material-ui/icons/Close';

class FullScreenDialog extends Component {

    constructor(props) {
    super(props);
    this.state = {};
    this.state.isOpen = props.isOpen;
    }

    handleClickOpen = () => {
    this.setState({ isOpen: true });
    };

    componentWillReceiveProps(nextProps) {
    this.setState( { isOpen: nextProps.isOpen } );
    }

    handleClose = () => {
    this.setState({ isOpen: false });
    };

    render() {
    return (
        <Dialog fullScreen open={this.state.isOpen} onClose={this.handleClose}>
        <AppBar >
        <Toolbar>
        <IconButton color="inherit" onClick={this.handleClose} aria-label="Close">
        <CloseIcon />
        </IconButton>
        <Typography variant="title" color="inherit" >
        A dialog
        </Typography>
        </Toolbar>
        </AppBar>
        <div>
        { /* Without both of these I don't see the content */ }
        <h1>The body of the dialog.</h1>
        <h1>The body of the dialog.</h1>
        </div>
        </Dialog>
    );
    }
}

class VerticalLinearStepper extends Component {

    constructor(props) {
    super(props);
    this.state = { noteDialogIsOpen: false, activeStep: 0 };
    }

    getSteps() {
    return ['First step', 'Second step'];
    }

    getStepContent(step, doc, query) {
    switch (step) {
    case 0:
        return (
        <Typography>
            { this.state.noteDialogIsOpen &&
            <FullScreenDialog isOpen={this.state.noteDialogIsOpen} query={query} doc={doc}/>
        }
        <Button onClick={this.openNoteDialog}>Open dialog.</Button>
        </Typography>
        )
    case 1:
        return "Something or other";
    default:
        return "Unsure";
    }
    }

    openNoteDialog = () => {
    this.setState( { noteDialogIsOpen: true } );
    }

    onClose() {
    this.setState( { noteDialogIsOpen: false } );
    }

    handleNext = () => {
    this.setState({
        activeStep: this.state.activeStep + 1,
    });
    };

    handleBack = () => {
    this.setState({
        activeStep: this.state.activeStep - 1,
    });
    };

    handleReset = () => {
    this.setState({
        activeStep: 0,
    });
    };

    render() {
    const { query, doc } = this.props;
    const steps = this.getSteps();
    const { activeStep } = this.state;

    return (
            <div>
        <Stepper activeStep={activeStep} orientation="vertical">
        {steps.map((label, index) => {
        return (
            <Step key={label}>
            <StepLabel>{label}</StepLabel>
            <StepContent>
            {this.getStepContent(index,doc,query)}
            <Button disabled={activeStep === 0} onClick={this.handleBack}>Back</Button>
            <Button onClick={this.handleNext}>
            {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
            </Button>
            </StepContent>
            </Step>
        );
        })}
        </Stepper>
        {activeStep === steps.length && (
        <Paper square elevation={0} >
        <Typography>All steps completed.</Typography>
        <Button onClick={this.handleReset}>
        Reset
        </Button>
        </Paper>
        )}
        </div>
    );
    }
}


ReactDOM.render(
    <div>
<VerticalLinearStepper query="asdasdasd" doc="asdadasdsa"/>
</div>,
  document.getElementById('react-container')

这可能是两个截然不同的问题,但它们是如此交织在一起我看不出它们之间的分离是如何让它们孤立地提出来的......

如果重要,package.json就在这里:

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^1.0.0",
    "@material-ui/icons": "^1.0.0",
    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-navigation": "^2.0.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
   "start": "PORT=3006 react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

这是使用create-react-app创建的,并经过修改以包含材料设计

1 个答案:

答案 0 :(得分:1)

这里我改变了两个类,使得Dialog无状态。通过这样做,您不需要复制数据,在VerticalLineStepper类中处理所有问题。

import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import ReactDOM from 'react-dom'
import React, { Component } from 'react';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import StepContent from '@material-ui/core/StepContent';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Dialog from '@material-ui/core/Dialog';
import CloseIcon from '@material-ui/icons/Close';

const FullScreenDialog = props => {
    return (
        <Dialog fullScreen open={props.isOpen} onClose={props.handleClose}>
            <AppBar >
                <Toolbar>
                    <IconButton color="inherit" onClick={props.handleClose} aria-label="Close">
                        <CloseIcon />
                    </IconButton>
                <Typography variant="title" color="inherit" >
                    A dialog
                </Typography>
                </Toolbar>
            </AppBar>
            <div>
            { /* Without both of these I don't see the content */ }
            <h1>The body of the dialog.</h1>
            <h1>The body of the dialog.</h1>
            </div>
        </Dialog>
    );
};

class VerticalLinearStepper extends Component {
    state = { 
        noteDialogIsOpen: false, 
        activeStep: 0 
    };

    getSteps() {
        return ['First step', 'Second step'];
    }

    getStepContent(step) {
        switch (step) {
            case 0:
                return (<Button onClick={this.openNoteDialog}>Open dialog.</Button>);
            case 1:
                return (<div>Something or other</div>);
            default:
                return  (<div>Unsure</div>);
        }
    }

    openNoteDialog = () => {
        this.setState({ noteDialogIsOpen: true });
    };

    closeDialog = () => {
        this.setState({ noteDialogIsOpen: false });
    };

    handleNext = () => {
        this.setState({
            activeStep: this.state.activeStep + 1,
        });
    };

    handleBack = () => {
        this.setState({
            activeStep: this.state.activeStep - 1,
        });
    };

    handleReset = () => {
        this.setState({
            activeStep: 0,
        });
    };

    render() {
        const steps = this.getSteps();
        const { activeStep } = this.state;

        return (
            <div>
                <Stepper activeStep={activeStep} orientation="vertical">
                    {steps.map((label, index) => {
                    return (
                        <Step key={label}>
                            <StepLabel>{label}</StepLabel>
                            <StepContent>
                                {this.getStepContent(index)}
                                <Button disabled={activeStep === 0} onClick={this.handleBack}>Back</Button>
                                <Button onClick={this.handleNext}>
                                    {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
                                </Button>
                            </StepContent>
                        </Step>
                    );
                    })}
                </Stepper>
                {activeStep === steps.length && (
                <Paper square elevation={0} >
                    <Typography>All steps completed.</Typography>
                    <Button onClick={this.handleReset}>
                        Reset
                    </Button>
                </Paper>
                )}
                <FullScreenDialog 
                    isOpen={this.state.noteDialogIsOpen} 
                    query={this.props.query} 
                    doc={this.props.doc}
                    handleClose={this.closeDialog}
                />
            </div>
        );
    }
}
ReactDOM.render( <div> <VerticalLinearStepper query="asdasdasd" doc="asdadasdsa"/> </div>, document.getElementById('react-container')
);