1
votes

I have a component with a form inside. The fields of the form rely on the state of the component that is updated within the onChange methods. Upon clicking the submit button, the component sends the form fields to the parent component, then updates the store. When the onChange method is called, 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."
I can't find the reason why a loop occurs inside the "MemberForm" component.
By debugging, however, I understood that at the change of state (not to the onChange function) the parent component "AggiungiSocio" is also rendered. However, the status of the "MemberForm" component is in no way related to the "AggiungiSocio" component.
What is causing this unexpected rendering? How can I avoid it?

The strange thing is that I also use the "MemberForm" component in the "ModificaSocio" component where I pass the form fields as properties. In this component "ModificaSocio" everything works correctly.

To recap
AddSocio -> MemberForm doesn't work
EditSocio -> MemberForm works

AggiungiSocio

import React, { Component, Fragment } from "react";
import {
  Row,
  Card,
  CardBody,
  CardTitle,
  CardSubtitle,
} from "reactstrap";
import IntlMessages from "../../../helpers/IntlMessages";
import { Colxx } from "../../../components/common/CustomBootstrap";
import MemberForm from "../../../containers/form/MemberForm";
import {injectIntl} from "react-intl";
import {connect} from "react-redux";
import {addMemberDetail} from "../../../redux/memberDetail/actions";
import {Socio} from "../../../models/socio";

class AggiungiSocio extends Component {
  constructor(props) {
    super(props);
  }

  aggiungiSocio = (socio) => {
    console.log(socio);
    this.props.addMemberDetail(socio);
  };

  render() {
    console.log("render aggiungi socio");
    const {
      loading
    } = this.props.memberDetail;
    return (
      <Fragment>
        <Row className="mb-4">
          <Colxx xxs="12">
            <Card>
              <CardBody>
                <CardTitle>
                  <IntlMessages id="pages.member.add-member"/>
                </CardTitle>
                <CardSubtitle>
                  <IntlMessages id="pages.member.add-member-description"/>
                </CardSubtitle>
                {loading ? (
                  <MemberForm onSubmit={this.aggiungiSocio}/>
                ) : (
                  <div className="loading"/>
                )
                }
              </CardBody>
            </Card>
          </Colxx>
        </Row>
      </Fragment>
    )
  }
}

const mapStateToProps = ({memberDetail}) => {
  return {
    memberDetail
  };
};
export default injectIntl(
  connect(
    mapStateToProps,
    {
      addMemberDetail
    }
  )(AggiungiSocio)
);

ModificaSocio

import React, { Component, Fragment } from "react";
import {
  Row,
  Card,
  CardBody,
  CardTitle
} from "reactstrap";
import IntlMessages from "../../../helpers/IntlMessages";
import { Colxx } from "../../../components/common/CustomBootstrap";
import "react-datepicker/dist/react-datepicker.css";
import MemberForm from "../../../containers/form/MemberForm";
import {injectIntl} from "react-intl";
import {connect} from "react-redux";
import {getMemberDetail, editMemberDetail} from "../../../redux/memberDetail/actions";

class ModificaSocio extends Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }
  modificaSocio = (socio) => {
    this.props.editMemberDetail(socio);
  }
  render() {
    const {
      member,
      loading
    } = this.props.memberDetail;
    return (
      <Fragment>
        <Row className="mb-4">
          <Colxx xxs="12">
            <Card>
              <CardBody>
                <CardTitle>
                  <IntlMessages id="pages.member.edit-member"/>
                </CardTitle>
                {loading ? (
                  <MemberForm member={member} onSubmit={this.modificaSocio}/>
                ): (
                  <div className="loading" />
                )
                }
              </CardBody>
            </Card>
          </Colxx>
        </Row>
      </Fragment>
    )
  }
}

const mapStateToProps = ({memberDetail}) => {
  return {
    memberDetail
  };
};
export default injectIntl(
  connect(
    mapStateToProps,
    {
      editMemberDetail,
      getMemberDetail
    }
  )(ModificaSocio)
);

MemberForm

import React, { Component } from "react";
import {
  Input,
  FormGroup,
  Label,
  Button,
  Form
} from "reactstrap";
import IntlMessages from "../../helpers/IntlMessages";
import DatePicker from "react-datepicker";
import "react-tagsinput/react-tagsinput.css";
import "react-datepicker/dist/react-datepicker.css";
import "rc-switch/assets/index.css";
import "rc-slider/assets/index.css";
import "react-rater/lib/react-rater.css";
import { Colxx } from "../../components/common/CustomBootstrap";
import Select from "react-select";
import CustomSelectInput from "../../components/common/CustomSelectInput";
import moment from "moment";
import {Socio} from "../../models/socio";

class MemberForm extends Component {
  constructor(props) {
    super(props);
    moment.locale("it");
    this.state = {
      member: (typeof this.props.member === 'undefined') ? Socio : this.props.member,
      selectQualifica: [{
        label: (typeof this.props.member === 'undefined') ? '' : this.props.member.qualifica,
        value: (typeof this.props.member === 'undefined') ? '' : this.props.member.qualifica
      }],
      selectTitolo: [{
        label: (typeof this.props.member === 'undefined') ? '' : this.props.member.titolo,
        value: (typeof this.props.member === 'undefined') ? '' : this.props.member.titolo
      }],
      dataDiNascita: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataDiNascita, "DD/MM/YYYY"),
      dataRichiestaIscrizione: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataRichiestaIscrizione, "DD/MM/YYYY"),
      dataAccettazione: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataAccettazione, "DD/MM/YYYY")
    }
  }

  handleChangeQualifica = qualifica => {
    this.setState({
      member: {
        ...this.state.member,
        qualifica: qualifica.value
      },
      selectQualifica: [{
        label: qualifica.value,
        value: qualifica.value
      }]
    });
  }
  handleChangeTitolo = titolo => {
    this.setState({
      member: {
        ...this.state.member,
        titolo: titolo.value
      },
      selectTitolo: [{
        label: titolo.value,
        value: titolo.value
      }]
    });
  }
  handlerChange = event => {
    const name = event.target.name;
    const value = event.target.value;
    if (value!=this.state.member[name]) {
      console.log("il valore è cambiato, aggiorno stato");
      console.log(value, this.state.member[name]);
      this.setState({
        member: {
          ...this.state.member,
          [name]: value
        }
      });
    } else {
      console.log("il valore è lo stesso!, non aggiorno stato");
      console.log(value, this.state.member[name]);
    }
  }
  handleChangeDataNascita = date => {
    this.setState({
      member: {
        ...this.state.member,
        dataDiNascita: date.format("DD/MM/YYYY")
      },
      dataDiNascita: date
    });
  };
  handleChangeDataRichiestaIscrizione = date => {
    this.setState({
      member: {
        ...this.state.member,
        dataRichiestaIscrizione: date.format("DD/MM/YYYY")
      },
      dataRichiestaIscrizione: date
    });
  };
  handleChangeDataAccettazione = date => {
    this.setState({
      member: {
        ...this.state.member,
        dataAccettazione: date.format("DD/MM/YYYY")
      },
      dataAccettazione: date
    });
  };
  handleSubmit = () => {
    this.props.onSubmit(this.state.member);
  }

  render () {
    console.log("render memberform");
    const {
      member,
      selectQualifica,
      selectTitolo,
      dataDiNascita,
      dataRichiestaIscrizione,
      dataAccettazione
    } = this.state;
    return (
      (member &&
        <Form>
          <FormGroup row>
            <Colxx sm={2}>
              <FormGroup>
                <Label for="memberTitle">
                  <IntlMessages id="member.idSocio"/>
                </Label>
                <Input
                  disabled
                  readOnly
                  type="text"
                  name="memberId"
                  id="memberId"
                  defaultValue={member.numeroSocio}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={2}>
              <FormGroup>
                <Label for="memberTitle">
                  <IntlMessages id="member.title"/>
                </Label>
                <Select
                  components={{Input: CustomSelectInput}}
                  className="react-select"
                  classNamePrefix="react-select"
                  name="form-field-name"
                  id="memberTitle"
                  options={[
                    {label: "Sig", value: "Sig", key: 0},
                    {label: "Sig.ra", value: "Sig.ra", key: 1}
                  ]}
                  value={selectTitolo}
                  onChange={this.handleChangeTitolo.bind(this)}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={3}>
              <Label for="memberName">
                <IntlMessages id="member.name"/>
              </Label>
              <Input
                type="text"
                name="nome"
                id="memberName"
                value={member.nome}
                onChange={this.handlerChange.bind(this)}
              />
            </Colxx>
            <Colxx sm={3}>
              <Label for="memberSurname">
                <IntlMessages id="member.surname"/>
              </Label>
              <Input
                type="text"
                name="cognome"
                id="memberSurname"
                value={member.cognome}
                onChange={this.handlerChange.bind(this)}
              />
            </Colxx>
            <Colxx sm={2}>
              <Label for="memberSurname">
                <IntlMessages id="member.birthday"/>
              </Label>
              <DatePicker
                dateFormat="DD/MM/YYYY"
                selected={dataDiNascita}
                onChange={this.handleChangeDataNascita.bind(this)}
                id="memberBirthday"/>
            </Colxx>
          </FormGroup>

          <FormGroup row>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="birth-comune">
                  <IntlMessages id="member.birth-comune"/>
                </Label>
                <Input
                  type="text"
                  name="comuneDiNascita"
                  id="birth-comune"
                  onChange={this.handlerChange.bind(this)}
                  value={member.comuneDiNascita}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={2}>
              <FormGroup>
                <Label for="birth-provincia">
                  <IntlMessages id="member.birth-provincia"/>
                </Label>
                <Input
                  type="text"
                  name="provinciaDiNascita"
                  id="birth-provincia"
                  onChange={this.handlerChange.bind(this)}
                  value={member.provinciaDiNascita}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={4}>
              <FormGroup>
                <Label for="residential-address">
                  <IntlMessages id="member.residential-address"/>
                </Label>
                <Input
                  type="text"
                  name="indirizzoDiResidenza"
                  id="residential-address"
                  onChange={this.handlerChange.bind(this)}
                  value={member.indirizzoDiResidenza}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="codice-fiscale">
                  <IntlMessages id="member.codice-fiscale"/>
                </Label>
                <Input
                  type="text"
                  name="codiceFiscale"
                  id="codice-fiscale"
                  onChange={this.handlerChange.bind(this)}
                  value={member.codiceFiscale}
                />
              </FormGroup>
            </Colxx>
          </FormGroup>

          <FormGroup row>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="data-richiesta-iscrizione">
                  <IntlMessages id="member.data-richiesta-iscrizione"/>
                </Label>
                <DatePicker
                  dateFormat="DD/MM/YYYY"
                  selected={dataRichiestaIscrizione}
                  onChange={this.handleChangeDataRichiestaIscrizione.bind(this)}
                  id="data-richiesta-iscrizione"/>
              </FormGroup>
            </Colxx>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="data-accettazione">
                  <IntlMessages id="member.data-accettazione"/>
                </Label>
                <DatePicker
                  dateFormat="DD/MM/YYYY"
                  selected={dataAccettazione}
                  onChange={this.handleChangeDataAccettazione.bind(this)}
                  id="data-accettazione"/>
              </FormGroup>
            </Colxx>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="payment">
                  <IntlMessages id="member.payment"/>
                </Label>
                <Input
                  type="text"
                  name="quotaVersata"
                  id="payment"
                  onChange={this.handlerChange.bind(this)}
                  value={member.quotaVersata}
                />
              </FormGroup>
            </Colxx>
            <Colxx sm={3}>
              <FormGroup>
                <Label for="qualification">
                  <IntlMessages id="member.qualification"/>
                </Label>
                <Select
                  components={{Input: CustomSelectInput}}
                  className="react-select"
                  classNamePrefix="react-select"
                  name="form-field-name"
                  id="qualification"
                  options={[
                    {label: "Socio", value: "Socio", key: 0},
                    {label: "Socio Fondatore", value: "Socio Fondatore", key: 1},
                    {label: "Consigliere", value: "Consigliere", key: 2}
                  ]}
                  value={selectQualifica}
                  onChange={this.handleChangeQualifica}
                />
              </FormGroup>
            </Colxx>
          </FormGroup>

          <Button color="primary" onClick={this.handleSubmit.bind(this)}>
            <IntlMessages id="pages.member.save"/>
          </Button>
        </Form>
      )
    )
  }
}
export default React.memo(MemberForm);
1

1 Answers

2
votes

You can't pass params to a function outside a function.

Correct:

< ... onChange={function} />

Not correct:

< ... onChange={function(params)} />

To use params you need to nest you function inside anonymous function :

< ... onChange={() => function(params)} />

In you case you want to write this :

< ... onChange={() => this.handlerChange.bind(this)} />