1
votes

I am trying to better structure my initial code.

https://codesandbox.io/s/jqvkqy29w

However I have problems with integrating children components. As an example here I show how I import a child component TopControls. Also I am not sure if I imported styles correctly.

Below I show the code of a main root component. The problem is that the page is empty and TopControls is not loaded. When I open console, I see the following warnings and errors (I use "react": "^16.6.0" and "react-dom": "^16.6.0").

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check your code at Main.js:113. in _default (at App.js:21) in App (at src/index.js:5)

react-dom.development.js:57 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of _default. at invariant (react-dom.development.js:57) at createFiberFromTypeAndProps (react-dom.development.js:10531) at createFiberFromElement (react-dom.development.js:10551) at createChild (react-dom.development.js:13811) at reconcileChildrenArray (react-dom.development.js:14080) at reconcileChildFibers (react-dom.development.js:14430) at reconcileChildren (react-dom.development.js:14817) at finishClassComponent (react-dom.development.js:15161) at updateClassComponent (react-dom.development.js:15096) at beginWork (react-dom.development.js:15980) at performUnitOfWork (react-dom.development.js:19102) at workLoop (react-dom.development.js:19143) at HTMLUnknownElement.callCallback (react-dom.development.js:147) at Object.invokeGuardedCallbackDev (react-dom.development.js:196) at invokeGuardedCallback (react-dom.development.js:250) at replayUnitOfWork (react-dom.development.js:18350) at renderRoot (react-dom.development.js:19261) at performWorkOnRoot (react-dom.development.js:20165) at performWork (react-dom.development.js:20075) at performSyncWork (react-dom.development.js:20049) at interactiveUpdates$1 (react-dom.development.js:20337) at interactiveUpdates (react-dom.development.js:2267) at dispatchInteractiveEvent (react-dom.development.js:5083) invariant @ react-dom.development.js:57 createFiberFromTypeAndProps @ react-dom.development.js:10531 createFiberFromElement @ react-dom.development.js:10551 createChild @ react-dom.development.js:13811 reconcileChildrenArray @ react-dom.development.js:14080 reconcileChildFibers @ react-dom.development.js:14430 reconcileChildren @ react-dom.development.js:14817 finishClassComponent @ react-dom.development.js:15161 updateClassComponent @ react-dom.development.js:15096 beginWork @ react-dom.development.js:15980 performUnitOfWork @ react-dom.development.js:19102 workLoop @ react-dom.development.js:19143 callCallback @ react-dom.development.js:147 invokeGuardedCallbackDev @ react-dom.development.js:196 invokeGuardedCallback @ react-dom.development.js:250 replayUnitOfWork @ react-dom.development.js:18350 renderRoot @ react-dom.development.js:19261 performWorkOnRoot @ react-dom.development.js:20165 performWork @ react-dom.development.js:20075 performSyncWork @ react-dom.development.js:20049 interactiveUpdates$1 @ react-dom.development.js:20337 interactiveUpdates @ react-dom.development.js:2267 dispatchInteractiveEvent @ react-dom.development.js:5083 index.js:1446 The above error occurred in the <_default> component: in _default (at App.js:21) in App (at src/index.js:5)

Root component "Main.js":

import React, { Component, Fragment } from 'react';
import TopControls from "./layout/single/TopControls"
import styles from "./layout/single/styles"


export default class extends 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,
          airport: "LEBL",
          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() {
    return <Fragment>

      <TopControls state={this.state} handleChange={this.handleChange} styles={styles} />

    </Fragment>
  }
}

styles.js

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,
    }
});

TopControls

import React, { Component, Fragment } from 'react';
import CssBaseline from '@material-ui/core/CssBaseline';
import Grid from '@material-ui/core/Grid';
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 TextField from '@material-ui/core/TextField';
import InputAdornment from '@material-ui/core/InputAdornment';

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

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

            <div className={this.props.styles.topControls}>
              <Grid container spacing={24}>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="outlined-simple-start-adornment"
                      className={this.props.styles.textField}
                      onChange={(event) => this.props.handleChange("holdingTime", event)}
                      value={this.props.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.props.handleChange("asma40", event)}
                      value={this.props.state.asma40}
                      type="number"
                      className={this.props.styles.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(seconds)</InputAdornment>,
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Additional ASMA 60"
                      onChange={(event) => this.props.handleChange("asma60", event)}
                      value={this.props.state.asma60}
                      type="number"
                      className={this.props.styles.textField}
                      margin="normal"
                      InputProps={{
                          startAdornment: <InputAdornment position="start">(seconds)</InputAdornment>,
                      }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                    required
                    name="plannedDep"
                    id="datetime-local"
                    onChange={(event) => this.props.handleChange("plannedDep", event)}
                    value={this.props.state.plannedDep}
                    label="Scheduled departure"
                    type="datetime-local"
                    className={this.props.styles.textField}
                    margin="normal"
                    InputLabelProps={{
                      shrink: true,
                    }}
                  />
                </Grid>
                <Grid item xs={2}>
                  <TextField
                      required
                      id="standard-number"
                      label="Planned turnaround"
                      onChange={(event) => this.props.handleChange("schedTurnd", event)}
                      value={this.props.state.schedTurnd}
                      type="number"
                      className={this.props.styles.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.props.handleChange("taxiInTime", event)}
                      value={this.props.state.taxiInTime}
                      type="number"
                      className={this.props.styles.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={this.props.styles.formControl}
                        margin="normal">
                        <InputLabel shrink htmlFor="wake-label-placeholder">
                                Wake
                        </InputLabel>
                        <Select
                          onChange={(event) => this.props.handleChange("wake", event)}
                          value={this.props.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.props.handleChange("temperature", event)}
                      value={this.props.state.temperature}
                      type="number"
                      className={this.props.styles.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.props.handleChange("visibility", event)}
                      value={this.props.state.visibility}
                      type="number"
                      className={this.props.styles.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.props.handleChange("windIntensity", event)}
                      value={this.state.windIntensity}
                      type="number"
                      className={this.props.styles.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.props.handleChange("arrivalDelay", event)}
                      value={this.props.state.arrivalDelay}
                      type="number"
                      className={this.props.styles.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.props.handleChange("distanceTarget", event)}
                      value={this.props.state.distanceTarget}
                      type="number"
                      className={this.props.styles.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>

        </Fragment>
      );
    }
}

Does somebody know where the problem is?

UPDATE:

I also tried this, but it gives me the same error:

const TopControls = () => (
  ...
);

export default TopControls;

App.js

import React, { Component } from "react";
import SelectionPage from "../modes/SelectionPage";
import Main from "../modes/Main";
import Main2 from "../modes/Main2";

class App extends Component {
  state = {
    renderView: 0
  };

  clickBtn = e => {
    console.log(e.target.value);
    this.setState({
      renderView: +e.target.parentNode.value
    });
  };

  render() {
    switch (this.state.renderView) {
      case 1:
        return <Main />;
      case 2:
        return <Main2 />;
      default:
        return <SelectionPage clickBtn={this.clickBtn} />;
    }
  }
}

export default App;
2
Can you please share app.js file as error is in that file as per error message ? - Sahil Arora
@SahilArora: Done. Please check my update. "SelectionPage" has 2 buttons. If a user clicks button 1, then it's redirected to "Main". Otherwise, it's redirected to "Main2". I only have problems with "Main". The code of "Main2" works fine, but it does not contain children - everything is in a single component. Therefore I am simplifying everything now. I started from "Main". Once it works, I will do the same for "Main2". - ScalaBoy
Can you provide Main.js and Main2.js, please? - JoshuaCS
@JosuéCortina: The code of Main.js is the one at the beginning of a post. The code of Main2.js is irrelevant to this issue and also it's very-very long. - ScalaBoy
@ScalaBoy It would be better if you create a fiddle or codesandbox that would reproduce your problem. That way it would be easier to debug and faster to resolve. - Ana Liza Pandac

2 Answers

1
votes

It seems you can not pass the state directly to the child component. You have two main ways to resolve that issue.

First option

You can to pass those values as props. Change state={this.state} by ...this.state (spread operator):

Main.js

render() {
    return <Fragment>
        <TopControls handleChange={this.handleChange} styles={styles} ...this.state/>
    </Fragment>
}

and get those values directly from the props in TopControls.js:

TopControls.js

render() {
    ...
    value={this.props.holdingTime}
    ...
}

Second option

Just use a different prop for passing the state:

Main.js

render() {
    return <Fragment>
        <TopControls myState={this.state} handleChange={this.handleChange} styles={styles} />
    </Fragment>
}

TopControls.js

render() {
    ...
    value={this.props.myState.holdingTime}
    ...
}
0
votes

Use the return statement under the render function in your app.js.

renderComponent() {
  switch (this.state.renderView) {
    case 1:
      return <Main />;
    case 2:
      return <Main2 />;
    default:
      return <SelectionPage clickBtn={this.clickBtn} />;
  }
}

render() {
    return this.renderComponent();
}

I modified the code a bit but it's the same idea.