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