MDBSelect Array of Options - Get Value Errors


Topic: MDBSelect Array of Options - Get Value Errors

datashield asked 6 years ago

Hello,

I am attempting to use the Array of Options version of the Select component found here. The code I'm using is displayed below. Currently when I try to insert a function for getValue or getTextContent it produces the error Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

getDateRange is being passed in from a parent element as returnValue. It is shown below so you can understand the function I am passing.

getDateRange(value) {
    const dateRange = value[0];
    this.setState({dateRange});
}

Below shows the component getDateRange is being passed to.

const Select = (props) => {
  const { menuItems, multiple, search, color, label, returnValue } = props;
  return (
    <div>
      <MDBSelect
        multiple={multiple}
        search={search}
        color={color}
        options={menuItems}
        getValue={returnValue}
        selected="Choose your option"
      />
      <label>{label}</label>
    </div>
  );
}; 

It appears that the issue is that during componentDidMount and componentDidUpdate this function is being run automatically. This bug does not appear to occur on the non-array option version of the component.


Anna Morawska staff answered 6 years ago

Hi there,

thank you that you put the effort to prepare the working example. You have just run into an interesting problem here - please noticed that you declared options2 constant in the render method. This variable is accolated in the memory every time method runs, and it lives there ONLY when that method is running. So every re-render creates a new allocation in the memory, and it is passed down as a prop to the child component as an entirely different constant. As you probably know, prop changes triggers re-render of the component, so again new constat is declared, component understand it as a prop change and we have a formula for infinite loop here.

To solve this, you have to declare the options array somewhere outside the render method. One option is inside a state object, but there are other solutions too. Please check out the code snippet bellow:

import React, { Component } from "react";
import { MDBRow, MDBCol, MDBSelect } from 'mdbreact';

// const options2 = [
//   { checked: true, value: '7', text: '7 Days' },
//   { value: '30', text: '30 Days' },
//   { value: '90', text: '90 Days' },
//   { value: 'custom', text: 'Custom Range' },
// ];

class Scans extends Component {
  constructor(props) {
    super(props);
    this.state = {
      options: [
        { checked: true, value: '7', text: '7 Days' },
        { value: '30', text: '30 Days' },
        { value: '90', text: '90 Days' },
        { value: 'custom', text: 'Custom Range' },
      ],


    };
    this.getDateRange = this.getDateRange.bind(this);
  }


  getDateRange(value) {
    const dateRange = value[0];
    this.setState({ dateRange });
  }

  options2 = [
      { checked: true, value: '7', text: '7 Days' },
      { value: '30', text: '30 Days' },
      { value: '90', text: '90 Days' },
      { value: 'custom', text: 'Custom Range' },
  ];


  render() {
    const { getDateRange } = this;
    const { options } = this.state;

    return (
      <div className="d-flex flex-column">
        <MDBRow>
          <MDBCol size="2">
            {/* // Update this instance of options to options2 to produce error */}
            <Select menuItems={this.options2} color="primary" label="Select" returnValue={getDateRange} />
          </MDBCol>
        </MDBRow>
      </div>
    );
  }

}

const Select = (props) => {
  const { menuItems, multiple, color, label, returnValue } = props;

  return (
    <div>
      <MDBSelect
        multiple={multiple}
        color={color}
        options={menuItems}
        getValue={returnValue}
        selected="Choose your option"
      />
      <label>{label}</label>
    </div>
  );
};


export default Scans;

Anna Morawska staff answered 6 years ago

Hi there,

I have tried to reproduce your problem, but it looks like everything works fine. Please try the code presented above:

import React, { Component } from "react";
import { MDBSelect } from "mdbreact";


class App extends Component {
  state = {
    options: [
      {
        checked: false,
        disabled: false,
        icon: null,
        text: "Option one"
      },
      {
        checked: false,
        disabled: false,
        icon: null,
        text: "Option two"
      },
      {
        checked: true,
        disabled: false,
        icon: null,
        text: "Option three"
      },
      {
        checked: false,
        disabled: false,
        icon: null,
        text: "Option four"
      }
    ]
  };


  getDateRange = (value) =>{
    const dateRange = value[0];
    this.setState({dateRange}, () => console.log(this.state));
  }

  render(){
    return (
      <div>
        <Select menuItems={this.state.options} multiple={false}  search={false}  color="primary" label="Select" returnValue={this.getDateRange}   />
      </div>
    );
  }

};



const Select = (props) => {
  const { menuItems, multiple,  color, label, returnValue } = props;

  return (
    <div>
      <MDBSelect
        multiple={multiple}
        color={color}
        options={menuItems}
        getValue={returnValue}
        selected="Choose your option"
      />
      <label>{label}</label>
    </div>
  );
};

export default App;

datashield answered 6 years ago

Hello,

I did more research into this thanks to the information you provided. The code you provided works perfectly and I have been able to now reproduce the error consistently.

It seems that options need to be fed in from the state for some reason. Below is a code example that contains the same options in this.state.options and options2. If you update the code to contain menuItems={options2} instead of menuItems={options} you should be able to reproduce the error.

I am also console logging both of the values to compare the differences, at this time I can see none. I also ran JSON.stringify on both of the variables and compared the output text and there does not appear to be a difference.

import React, { Component } from "react";
import { MDBRow, MDBCol, MDBSelect } from 'mdbreact';

class Scans extends Component {
  constructor(props) {
    super(props);
    this.state = {
      options: [
        { checked: true, value: '7', text: '7 Days' },
        { value: '30', text: '30 Days' },
        { value: '90', text: '90 Days' },
        { value: 'custom', text: 'Custom Range' },
      ],
    };

    this.getDateRange = this.getDateRange.bind(this);
  }


  getDateRange(value) {
    const dateRange = value[0];
    this.setState({ dateRange });
  }

  render() {
    const { getDateRange } = this;
    const { options } = this.state;
    const options2 = [
      { checked: true, value: '7', text: '7 Days' },
      { value: '30', text: '30 Days' },
      { value: '90', text: '90 Days' },
      { value: 'custom', text: 'Custom Range' },
    ];

    console.log(options);
    console.log(options2);
    return (
      <div className="d-flex flex-column">
        <MDBRow>
          <MDBCol size="2">
            // Update this instance of options to options2 to produce error
            <Select menuItems={options} color="primary" label="Select" returnValue={getDateRange} />
          </MDBCol>
        </MDBRow>
      </div>
    );
  }

}

const Select = (props) => {
  const { menuItems, multiple, color, label, returnValue } = props;

  return (
    <div>
      <MDBSelect
        multiple={multiple}
        color={color}
        options={menuItems}
        getValue={returnValue}
        selected="Choose your option"
      />
      <label>{label}</label>
    </div>
  );
};


export default Scans;

datashield answered 6 years ago

Hello,

Thank you so much for explaining that. Having never run into it before I would have never guessed that was the issue. Again, thank you.


Anna Morawska staff commented 6 years ago

Happy to help :) Best, Ania


run into the exact same issue using #next as a version from gitlab, I'm using nextjs.

I was preparing the options in render and I run into maximum update depth ....

In componentDidMount I'm fetching my data and then programmatically build the options from the response.

example:

componentDidMount() {
let id = localStorage.getItem("_id");
axiosCallApi.get(`${apiUrl}/account/user/${id}`).then(response => {
  const user = response.data;
  const ranges = [
    {
      checked: user.comRange === 5000,
      disabled: false,
      icon: null,
      value: 5000,
      text: 5
    },
    {
      checked: user.comRange === 10000,
      disabled: false,
      icon: null,
      value: 10000,
      text: 10
    }
  ];
  this.setState({ user, ranges });
});
}

 handleDistanceChange = value => {
this.setState(
  update(this.state, {
    user: {
      comRange: {
        $set: value[0]
      }
    }
  })
);

};

  <MDBSelect
      color="primary"
      getValue={value =>
         this.handleDistanceChange(value)
      }
      options={this.state.ranges}
      selected="Choose your option"
  />

The issue comes from getValue, it seems it infinite runs if the options are inside render()


Anna Morawska staff answered 6 years ago

Hi @stathisntonas,

unfortunately, it's a bug, I've added it our TODO list - we will try to fix this as soon as possible. Sorry for the inconvenience.

Best regards,

Ania


dev.penpenn answered 5 years ago

is this fu****g fix? i just encounter this tsk


Piotr Glejzer staff commented 5 years ago

Hi,
may you show me your code? I don't think it is fixed but I will try to help. Thanks.


Ahmadjohn answered 5 years ago

Hi any news on fixing this?

I'm trying to make a custom selection where you choose a different outfit option and it will give a different price value on the second form dropdown

Top

class Dashboard extends Component {
  constructor(props) {
    super(props);
    this.state = {
  outfits: "",
  outfits_budget: "",
  outfits14: "block",
  outfits17: "none",
  outfits2: "none",

Mid

        };

    this.onChange = this.onChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }

  onChange(e) {
    this.setState({ [e.target.name]: e.target.value });
  }

  componentDidMount() {
    if (!this.props.auth.isAuthenticated) {
      this.props.history.push("/login");
    }
    if (this.props.auth.user.user_type == "1") {
      this.props.history.push("/admin");
    }
  }

  filterOutfits(e) {
    e.preventDefault();
    this.setState({ outfits: e.target.value, outfits_budget: "" });
    switch (e.target.value) {
      case "1 OUTFIT 4HRS":
        this.setState({
          outfits_budget: "",
          outfits14: "block",
          outfits17: "none",
          outfits2: "none",
          outfits3: "none",
          outfits4: "none"
        });

  onSubmit(e) {
    e.preventDefault();
    const data = {
      outfits: this.state.outfits,
      outfits_budget: this.state.outfits_budget,
    };
    // console.log(data);
    this.props.surveySubmit(data);
  }

bottom

No. of Outfits 1 OUTFIT 4HRS 1 OUTFIT 7HRS 2 OUTFIT 3 OUTFIT 4 OUTFIT
          <div className="form-group col-md-6">
            <MDBSelect
              label="Outfits Budget"
              name="outfits_budget"
              id="outfits_budget"
              required
              onChange={this.onChange}
              color="dark"
            >
              <MDBSelectInput selected="Outfits Budget" />
              <MDBSelectOptions>
                <MDBSelectOption disabled>Outfits Budget</MDBSelectOption>
                <MDBSelectOption
                  value="600 - 990"
                  style={{ display: this.state.outfits14 }}
                >
                  $600 - $990
                  </MDBSelectOption>
                <MDBSelectOption
                  value="990 - 1200"
                  style={{ display: this.state.outfits14 }}
                >
                  $990 - $1200
                  </MDBSelectOption>
                <MDBSelectOption
                  value="1600 - 1900"
                  style={{ display: this.state.outfits17 }}
                >
                  $1600 - $1900
                  </MDBSelectOption>
                <MDBSelectOption
                  value="2100 - 2600"
                  style={{ display: this.state.outfits17 }}
                >
                  $2100 - $2600
                  </MDBSelectOption>
                <MDBSelectOption
                  value="2300 - 2600"
                  style={{ display: this.state.outfits2 }}
                >
                  $2300 - $2600
                  </MDBSelectOption>



              </MDBSelectOptions>

Piotr Glejzer staff commented 5 years ago

We will rewrite MDBSelect to a new component in the nearly future. There are a lot of bugs and we know about that. We are very sorry about these problems and we will fix this for sure with the new Material Select. Have a nice day.


Use react-select, it's free and simple. I'm sad to be a PRO user and not be able to use


Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue
  • User: Free
  • Premium support: No
  • Technology: MDB React
  • MDB Version: 4.8.4
  • Device: Laptop
  • Browser: Google Chrome 71.0.3578.98
  • OS: Windows 10
  • Provided sample code: No
  • Provided link: Yes