Geoff_S
Geoff_S

Reputation: 5107

react app, load components based on dropdown option

Currently, in my react app, I have a parent component which is housing two child components (TrendsComonent and BaselineComponent) and they load successfully. The parent component also has a dropdown component loaded.

What i want to do is set a default Component to load in the parent initially, but I want to map each child component to the dropdown options.

For instance, when the parent component is visited I would like to have TrendsComponent load initially as the default but have it tied to the trends dropdown option, as well as have the BaselineComponent mapped to the baseline option of the dropdown.

Basically, I just want to load components based on the dropdown option as opposed to all at once

TrendComponent.js

import React, { Component } from "react";
import Chart from "react-apexcharts";
import { StyleSheet, css } from 'aphrodite/no-important';

const styles = StyleSheet.create({
    TrendsComponent: {
      display: 'flex',
      flexDirection: 'row',
      justifyContent: 'center',
      textAlign: 'center'
    },
    TrendsTitle: {
      display: 'flex',
      flexDirection: 'row',
      justifyContent: 'center',
      alignItems:'center'
    }
});

class TrendsComponent extends Component {

  render() {
    return (
      <div>
      <div className={css(styles.TrendsTitle)}>
        Net Calories 
      </div>
      <div className={css(styles.TrendsComponent)}>
        <div className={css(styles.TrendsComponent)}>
          <div className="mixed-chart">

            <Chart
            />
          </div>
        </div>
      </div>
      </div>
    );
  }
}

export default TrendsComponent;

BaselineComponent.js

import React, { Component } from "react";
import Chart from "react-apexcharts";
import { StyleSheet, css } from 'aphrodite/no-important';

const styles = StyleSheet.create({
    TrendsComponent: {
      display: 'flex',
      flexDirection: 'row',
      justifyContent: 'center',
      textAlign: 'center'
    },
    TrendsTitle: {
      display: 'flex',
      flexDirection: 'row',
      justifyContent: 'center',
      alignItems:'center'
    }
});

class BaselineComponent extends Component {

  render() {
    return (
      <div>
      <div className={css(styles.TrendsTitle)}>
        Net Calories 
      </div>
      <div >
        <div >
          <div className="mixed-chart">

            <Chart
            />
          </div>
        </div>
      </div>
      </div>
    );
  }
}

export default BaselineComponent;

Then I have the parent component that currently holds that component as well as the dropdown

trendparent.js

import React, { Component } from "react";
import Chart from "react-apexcharts";
import { StyleSheet, css } from 'aphrodite/no-important';
import TrendsComponent from './Trendscomponent';
import BaselineComponent from './BaselineComponent';
import TrendDropdownComponent from './TrendDropdownComponent';

class trendparent extends Component {

  render() {
    return (
      <div>
      <div className={css(styles.TrendsTitle)}>
        Net Calories 
      </div>
      <div>
        <div>
          <div>
            <TrendsComponent />
            <BaselineComponent />
          </div>
        </div>
      </div>

      <div style={{height:50}}>
      </div>

      <div>
      <TrendDropdownComponent />
      </div>
      </div>
    );
  }
}

export default trendparent;

dropdown.js

import React, { Component } from "react";
import { makeStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';

const useStyles = makeStyles(theme => ({
  formControl: {
    margin: theme.spacing(1),
    minWidth: 220,
  },
  selectEmpty: {
    marginTop: theme.spacing(2),
  },
}));

export default function SimpleSelect() {
  const classes = useStyles();
  const [age, setAge] = React.useState('');

  const inputLabel = React.useRef(null);


  const handleChange = event => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-label">Calories</InputLabel>
        <Select
          labelId="demo-simple-select-label"
          id="demo-simple-select"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value={10}>Trend</MenuItem>
          <MenuItem value={20}>Baseline</MenuItem>

        </Select>
      </FormControl>
    </div>
  );
}

Upvotes: 0

Views: 414

Answers (1)

Telmo Dias
Telmo Dias

Reputation: 4168

The question here is what you really mean by "load".

1) If what you mean by "load" means actually to only render a component that was statically imported (already declared in the beginning of the file), then all you have to do is set some default value on the state i.e. :

state = {
   renderComponentX: false
}

And then on the dropdown change method change the state to true:

setState({"renderComponentX":true})

And inside render have a condition as :

{this.state.renderComponentX && <ComponentX />}

2) If on the other hand what you want is really to dynamically load components then it's a bit more complicated :

You need to create a component that Asynchronously loads other components. I normally create an array of randomKeys on the state of the component, at the constructor:

constructor(props) {
    super(props);

    // Dynamic Key Generation for dynamic view loading
    let randomKeys = [];
    while(randomKeys.length < 10){
        let y = Math.random()*10000;
        if(randomKeys.indexOf(y) === -1) randomKeys.push(y);
    }
    this.state = {
        randomKeys
    };
}

So that each of the new imported components will have a different Key. In this case it's hardcoded to 0 but if you want to make this inside an iterator, you would have to create a variable to act as a counter to keep updating the index such as randomKeys[i] where i needs to grow from 0 to the length of components you want to import. Also you need to make sure to generate enough keys in the constructor; this one is only generating 10, because this was for manual import, not within an iterator.

<AsyncComponent key={this.state.randomKeys[0]} getComponent={() => import('../Login/Login.js')} />

and my AsyncComponent looks like this :

import React from 'react';
import PropTypes from 'prop-types';

export default class AsyncComponent extends React.Component {

    state = {
        AsyncModule: null,

    };

    componentDidMount() {
        let that = this;
        this.unmounted = false;

        this.props.getComponent()
        .then(module => {
            console.log("AsyncComponent loaded module:",module);
            return module.default;
        })
        .then(AsyncModule => {
            if(that.unmounted!==true) {
                that.setState({AsyncModule})
            }   
        });
    }

    componentDidUpdate() {

    }

    componentWillUnmount() {
        this.unmounted = true;
    }

    render() {
        const {loader, ...childProps} = this.props;
        const {AsyncModule} = this.state;

        if(AsyncModule) {
            return (<AsyncModule {...childProps} />)
        }

        if(loader) {
            console.log('loader = ',loader);
            return <div>Loading...</div>;
        }

        return null;
    }
}

AsyncComponent.propTypes = {
    getComponent: PropTypes.func,
    loader: PropTypes.element
};

Upvotes: 1

Related Questions