如何正确地将一个组件的代码拆分为多个子组件?

时间:2019-01-15 11:18:13

标签: javascript reactjs material-ui

我有一个组件MyComp,其中包含一个长代码,我想将其分为3个组件。

我的目标是获得MyComp的以下设置。我感谢一般准则和答案中的一个小例子:

import React, { Component, Fragment } from 'react'
import { TopControls, Main, BottomBar } from './layouts'

export default class extends Component {
  render() {
    return <Fragment>

      <TopControls />

      <Main />

      <BottomBar />

    </Fragment>
  }
}

下面,我提供代码MyComp。我的特别疑问是我应该将const styles和所有功能(例如updateDelay等,因为它们将在组件TopControlsMainBottomBar之间共享:

import React from 'react';
import PropTypes from 'prop-types';
import withStyles from '@material-ui/core/styles/withStyles';
import CssBaseline from '@material-ui/core/CssBaseline';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import Icon from '@material-ui/core/Icon';
import TextField from '@material-ui/core/TextField';
import LinePlot from '../chart/LinePlot';
import BrushBarPlot from '../chart/BrushBarPlot';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardContent from '@material-ui/core/CardContent';
import AppBar from '@material-ui/core/AppBar';
import InputAdornment from '@material-ui/core/InputAdornment';


const styles = theme => ({
    header: {
        borderBottom: 'solid 1px rgba(0,0,0,0.4)',
        backgroundColor: '#253069',
        color: '#d2d6ef',
        overflow: 'hidden',
        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
        position: 'relative',
        height: '12%'
    },
    h1: {
        fontSize: '30px',
        textAlign: 'center',
        fontFamily: 'sans-serif',
        lineHeight: '1.45em',
        webkitFontSmoothing: 'antialiased'
    },
    h3: {
        fontSize: '20px',
        textAlign: 'left',
        fontFamily: 'sans-serif',
        lineHeight: '1.45em',
        marginLeft: theme.spacing.unit*2,
        webkitFontSmoothing: 'antialiased'
    },
    appBar: {
      top: 'auto',
      bottom: 5,
      height: '10%'
    },
    toolbar: {
      alignItems: "center",
      justifyContent: "space-between"
    },
    textField: {
      fontSize: '12px',
      margin: theme.spacing.unit,
      minWidth: 120
    },
    formControl: {
      fontSize: '12px',
      margin: theme.spacing.unit,
      minWidth: 120
    },
    predictedDelay: {
        marginTop: '10px',
        position: 'relative',
        minWidth: 350,
        maxWidth: 350,
        textAlign: 'center',
        fontFamily: 'sans-serif',
        backgroundColor: 'rgb(225, 0, 80)'
    },
    predictedDelayText: {
        fontSize: '18px'
    },
    topControls: {
        borderBottom: '1px solid #ddd',
        height: '25%',
        boxShadow: '0 1px 4px rgba(0,0,0,0.08)',
        background: 'white',
        marginLeft: theme.spacing.unit*2,
        marginRight: theme.spacing.unit*2,
    },
    mainPart: {
        webkitJustifyContent: 'space-between',
        justifyContent: 'space-between',
        marginTop: '30px',
        marginBottom: '50px',
        paddingTop: '2px',
        position: 'relative',
        backgroundColor: '#f7f7f7'
    },
    card: {
        maxWidth: 200
    },
    paper: {
        backgroundColor: '#f7f7f7',
        padding: theme.spacing.unit * 2,
        height: '70%',
        marginLeft: theme.spacing.unit * 2,
        marginRight: theme.spacing.unit * 2,
    },
    buttons: {
        display: 'flex',
        justifyContent: 'flex-end',
    },
    rightIcon: {
        marginLeft: theme.spacing.unit,
    },
    button: {
        marginTop: theme.spacing.unit * 3,
        marginLeft: theme.spacing.unit,
    }
});


class MyComp extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            holdingTime: 1,
            plannedDep: "2017-05-24T10:30",
            schedTurnd: 45,
            asma40: 100,
            asma60: 500,
            taxiInTime: 9.50,
            wake: 84.73,
            temperature: 20,
            visibility: 5999.66,
            windIntensity: 8.0,
            arrivalDelay: 5,
            distanceTarget: 500,,
            delay: 0,
            delay_probability: 0,
            delay_cat: "NA",
            chartDataWake: [],
            chartDataTurnaround: [],
            chartDataArrivalDelay: [],
            chartDataDistanceTarget: [],
            labelWidth: 0
        };
        this.handleChange = this.handleChange.bind(this);
    };

    componentDidMount() {
        this.fetchData();
    };

    updateDelay(predicted_delay,delay_probability) {
        this.state.chartDataWake = [...this.state.chartDataWake, {wake: this.state.wake===84.73 ? "H" : (this.state.wake===14.78 ? "M" : "L"), delay: predicted_delay}];
        this.state.chartDataTurnaround = [...this.state.chartDataTurnaround, {turnaround: this.state.schedTurnd, delay: predicted_delay}];
        this.state.chartDataArrivalDelay = [...this.state.chartDataArrivalDelay, {arrivalDelay: this.state.arrivalDelay, delay: predicted_delay}];
        this.state.chartDataDistanceTarget = [...this.state.chartDataDistanceTarget, {distanceTarget: this.state.distanceTarget, delay: predicted_delay}];
        this.setState({
          delay: predicted_delay,
          delay_probability: delay_probability,
          delay_cat: predicted_delay===0 ? "<15" : (predicted_delay===1 ? "[15; 45]" : ">45")
        });
    };

    fetchData = () => {
        const url = "http://localhost:8000?"+
          'holdingTime='+this.state.holdingTime+
          '&plannedDep='+this.state.plannedDep+
          '&schedTurnd='+this.state.schedTurnd+
          '&asma40='+this.state.asma40+
          '&asma60='+this.state.asma60+
          '&taxiInTime='+this.state.taxiInTime+
          '&wake='+this.state.wake+
          '&temperature='+this.state.temperature+
          '&visibility='+this.state.visibility+
          '&windIntensity='+this.state.windIntensity+
          '&arrivalDelay='+this.state.arrivalDelay+
          '&distanceTarget='+this.state.distanceTarget;

        fetch(url, {
          method: "GET",
          dataType: "JSON",
          headers: {
            "Content-Type": "application/json; charset=utf-8",
          }
        })
        .then((resp) => {
          return resp.json()
        })
        .then((data) => {
          this.updateDelay(data.prediction,data.probability)
        })
        .catch((error) => {
          console.log(error, "catch the hoop")
        })
    };

    handleChange = (name, event) => {
      this.setState({
        [name]: event.target.value
      }, () => {
        console.log("plannedDep",this.state.plannedDep)
      });
    };


    handleReset = () => {
        this.setState({
            chartDataWake: [],
            chartDataTurnaround: [],
            chartDataArrivalDelay: [],
            chartDataDistanceTarget: [],
            delay: 0,
            delay_probability: 0,
            delay_cat: "NA"
        });
    };

    render() {
        const { classes } = this.props;

        return (
        <React.Fragment>
            <CssBaseline />

            <div className={classes.header}><h1 className={classes.h1}>Prediction of departure delays for a single flight</h1></div>

            <div className={classes.topControls}>
              <Grid container spacing={24}>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="outlined-simple-start-adornment"
                      className={classes.textField}
                      onChange={(event) => this.handleChange("holdingTime", event)}
                      value={this.state.holdingTime}
                      margin="normal"
                      label="Holding time"
                      type="number"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(seconds)</InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Additional ASMA 40"
                      onChange={(event) => this.handleChange("asma40", event)}
                      value={this.state.asma40}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(minutes)</InputAdornment>,
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Additional ASMA 60"
                      onChange={(event) => this.handleChange("asma60", event)}
                      value={this.state.asma60}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(minutes)</InputAdornment>,
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                    required
                    name="plannedDep"
                    id="datetime-local"
                    onChange={(event) => this.handleChange("plannedDep", event)}
                    value={this.state.plannedDep}
                    label="Scheduled departure"
                    type="datetime-local"
                    className={classes.textField}
                    margin="normal"
                    InputLabelProps={{
                      shrink: true,
                    }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Planned turnaround"
                      onChange={(event) => this.handleChange("schedTurnd", event)}
                      value={this.state.schedTurnd}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(minutes)</InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Taxi-In time"
                      onChange={(event) => this.handleChange("taxiInTime", event)}
                      value={this.state.taxiInTime}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(seconds)</InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
              </Grid>
              <Grid container spacing={24}>
                <Grid item xs={2}>
                    <FormControl
                        required
                        className={classes.formControl}
                        margin="normal">
                        <InputLabel shrink htmlFor="wake-label-placeholder">
                                Wake
                        </InputLabel>
                        <Select
                          onChange={(event) => this.handleChange("wake", event)}
                          value={this.state.wake}
                          input={<Input name="wake" id="wake-label-placeholder" />}
                          displayEmpty
                          name="wake"
                        >
                          <MenuItem value={84.73}>Heavy</MenuItem>
                          <MenuItem value={14.78}>Medium</MenuItem>
                          <MenuItem value={0.49}>Light</MenuItem>
                        </Select>
                    </FormControl>
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Temperature"
                      onChange={(event) => this.handleChange("temperature", event)}
                      value={this.state.temperature}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(Celsius)</InputAdornment>,
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Visibility"
                      onChange={(event) => this.handleChange("visibility", event)}
                      value={this.state.visibility}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start"></InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Wind Intensity"
                      onChange={(event) => this.handleChange("windIntensity", event)}
                      value={this.state.windIntensity}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(knots)</InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Arrival delay"
                      onChange={(event) => this.handleChange("arrivalDelay", event)}
                      value={this.state.arrivalDelay}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(minutes)</InputAdornment>,
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Distance to target"
                      onChange={(event) => this.handleChange("distanceTarget", event)}
                      value={this.state.distanceTarget}
                      type="number"
                      className={classes.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(km)</InputAdornment>
                      }}
                      onInput = {(e) =>{
                          e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
                      }}
                  />
                </Grid>
              </Grid>
            </div>




            <main className={classes.mainPart}>
                <Grid container spacing={24}>
                    <Grid item xs={12} sm={6} >
                        <div className={classes.h3}>Wake group</div>
                        <BrushBarPlot chartData={this.state.chartDataWake} varname="wake"/>
                    </Grid>
                    <Grid item xs={12} sm={6} className={classes.card}>
                        <div className={classes.h3}>Turnaround time</div>
                        <LinePlot chartData={this.state.chartDataTurnaround} varname="turnaround"/>
                    </Grid>
                    <Grid item xs={12} sm={6} >
                        <div className={classes.h3}>Arrival delay of a flight</div>
                        <LinePlot chartData={this.state.chartDataArrivalDelay} varname="arrivalDelay"/>
                    </Grid>
                    <Grid item xs={12} sm={6} className={classes.card}>
                        <div className={classes.h3}>Distance to target</div>
                        <LinePlot chartData={this.state.chartDataDistanceTarget} varname="distanceTarget"/>
                    </Grid>
                </Grid>
            </main>

            <AppBar position="fixed" color="primary" className={classes.appBar}>
              <div className={classes.toolbar}>
                  <Grid container spacing={24}>
                      <Grid item xs={6} sm={3}>
                          <Button variant="contained" color="primary" onClick={this.fetchData} className={classes.button}>
                              Predict
                              <Icon className={classes.rightIcon}>send</Icon>
                          </Button>
                          <Button variant="contained" color="primary" onClick={this.handleReset} className={classes.button}>
                              Reset
                              <Icon className={classes.rightIcon}>clear</Icon>
                          </Button>
                      </Grid>
                      <Grid item xs={6} sm={2}>
                          <Card className={classes.predictedDelay}>
                              <CardActionArea>
                                  <CardContent>
                                      <div className={classes.predictedDelayText}>
                                          Delay class: {this.state.delay_cat} <span> </span>
                                          (Prob.: {this.state.delay_probability})
                                      </div>
                                  </CardContent>
                              </CardActionArea>
                          </Card>
                      </Grid>
                  </Grid>
              </div>

            </AppBar>
        </React.Fragment>
        );
    }
}

MyComp.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(MyComp);

1 个答案:

答案 0 :(得分:0)

只需将通用功能作为prop传递,那么所有孩子都将达到:

render() {
  return (
    <Fragment>
      <TopControls onUpdateDelay={this.updateDelay} />
      <Main onUpdateDelay={this.updateDelay} />
      <BottomBar onUpdateDelay={this.updateDelay} />
    </Fragment>
  ) 
}

此外,您可以将所有常见样式放入外部styles.css文件中,并将其导入所有组件中,如下所示:

import '../relative/path/to/styles.css';

class MyComp extends React.Component {...

您应该正确设置Webpack,包裹等打包程序,以启用CSS导入。