React - Infinite loop when changing state from onChange - reactjs

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

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)} />

Related

React.js : TypeError: values.hobbies is not iterable and also getting error while displaying the college name on the userslist page

1.How can I iterate over hobbies array in AddUser.js file ,I am not getting it. I tried using map function but it is throwing some iterable error.How can I iterate over a checkbox?
2.When I am trying to display the college name in UsersList.js file it is throwing errors and when I visit the userlist page, the page appears to be blank.It displays nothing.
Thanks in advance.
Here's the code :
AddUser.js
import React, { useState, useContext, useEffect } from 'react'
import {
Form,
FormGroup,
Label,
Input,
Button,
Col
} from 'reactstrap'
import { GlobalContext } from '../context/GlobalState'
import { useHistory } from 'react-router-dom'
import { v4 as uuid } from 'uuid'
import axios from 'axios'
const AddUser = () => {
const { addUser } = useContext(GlobalContext)
const history = useHistory()
const [values, setValues] = useState({
fullname: '',
birthdate: new Date().toJSON().slice(0, 10).replace(/-/g, '/'),
address: '',
gender: '',
college: [],
hobbies: []
})
const handleSubmit = () => {
const newUser = {
id: uuid(),
fullname: values.fullname,
birthdate: values.birthdate,
address: values.address,
college: values.college,
gender: values.gender,
hobbies: values.hobbies
}
addUser(newUser)
history.push('/userslist')
}
useEffect(() => {
async function fetchData(name) {
const response = await axios.get(`http://universities.hipolabs.com/search`, {
params: { name }
}
)
console.log(response.data)
setValues({ college: response.data })
}
fetchData('Middle')
}, [])
const handleChange = e => {
setValues({
...values, [e.target.name]: e.target.value
})
}
const handleCheckboxChange = event => {
let newArray = [...values.hobbies, event.target.id];
if (values.hobbies.includes(event.target.id)) {
newArray = newArray.filter(hobby => hobby !== event.target.id);
}
setValues({
hobbies: newArray
})
}
return (
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label>Name</Label>
<Input type='text' placeholder='Enter Name'
value={values.fullname}
onChange={handleChange}
name='fullname'
/>
</FormGroup>
<FormGroup>
<Label>BirthDate</Label>
<Input type='date'
placeholder='Enter BirthDate'
value={values.birthdate} onChange={handleChange}
name='birthdate'
/>
</FormGroup>
<FormGroup>
<Label>Address</Label>
<Input type='textarea'
placeholder='Enter Address'
value={values.address} onChange={handleChange}
name='address' />
</FormGroup>
<FormGroup row>
<Label for="exampleSelect" sm={2}>Select College Name</Label>
<Col sm={10}>
<Input type="select" name="select" id="exampleSelect" onChange={handleChange}>
<option value='' disabled hidden>College List</option>
{values.college &&
values.college.map((col, index) => {
return <option value={col.name} key={index + 1}>{col.name}</option>
})
}
</Input>
</Col>
</FormGroup>
<FormGroup tag="fieldset">
<Label>Please Select Gender</Label>
<FormGroup>
<Label>
<Input type='radio' name='gender'
onChange={handleChange}
value='Male'
checked={values.gender === "Male"}
/>
Male
</Label>
</FormGroup>
<FormGroup>
<Label >
<Input type='radio' name='gender'
onChange={handleChange}
value='Female'
checked={values.gender === "Female"} />
Female
</Label>
</FormGroup>
<FormGroup>
<Label>
<Input type='radio' name='gender'
onChange={handleChange}
value='Other'
checked={values.gender === 'Other'} />
Other
</Label>
</FormGroup>
</FormGroup>
<FormGroup>
<FormGroup>
<Input type="checkbox" name="travelling" value='travelling' id="exampleCheck" onChange=
{handleCheckboxChange} />
<Label for="exampleCheck">Travelling</Label>
</FormGroup>
<FormGroup>
<Input type="checkbox" name="reading" value='reading' id="exampleCheck" onChange=
{handleCheckboxChange} />
<Label for="exampleCheck">Reading</Label>
</FormGroup>
<FormGroup>
<Input type="checkbox" name="gaming" value='gaming' id="exampleCheck" onChange=
{handleCheckboxChange} />
<Label for="exampleCheck">Gaming</Label>
</FormGroup>
</FormGroup>
{values.hobbies && values.hobbies.map((hobby, index) => {
return <p key={index}>{hobby}</p>
})}
<Button type='submit'>Submit</Button>
</Form>
)
}
export default AddUser
UsersList.js
import React, { useContext } from 'react'
import { Link } from 'react-router-dom'
import {
Button,
Table
} from 'reactstrap'
import { GlobalContext } from '../context/GlobalState'
const UsersList = () => {
const { users, removeUser } = useContext(GlobalContext)
console.log(users)
return (
<Table dark>
{users.length > 0 ? <>
{users.map((user) => (<>
<thead>
<tr>
<th>#</th>
<th> Name</th>
<th>BirthDate</th>
<th>Address</th>
<th>Gender</th>
<th>College</th>
<th>Update Action</th>
<th>Delete Action</th>
</tr>
</thead>
<tbody >
<tr key={user.id}>
<th scope="row">{user.id}</th>
<td>{user.fullname}</td>
<td>{user.birthdate}</td>
<td>{user.address}</td>
<td>{user.gender}</td>
<td>{user.college}</td>
<td><Link className='btn btn-warning mr-1' to={`/edituser/${user.id}`}>Edit</Link></td>
<td><Button color='danger' onClick={()=>removeUser(user.id)}>Delete</Button></td>
</tr>
</tbody></>))}
</> : <p>No Users Found</p>}
</Table>
)
}
export default UsersList
Create a method in the component and pass the values object to method and loop the college values and then create the options inside the loop and return them.
For example :
createCollegeOptions(var values){
let collegeOptions = [];
for(let i =0; i < values.college.length; i++){
collegeOptions.push(
<option key={i} value = {values[i].college}>{values[i].college}</option>
)
}
return collegeOptions;
}
Call the above method in render like this :
<div>
<select>
{this.createCollegeOptions()}
</select>
</div>

Redux form, loses focus after keypress?

Im experiencing a bit of a weird problem.
I have a form where I have an input field, but after i trigger an onChange event with a keypress it loses focus from the input field...
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
I've seen this example setup where the problem occurs (https://codepen.io/invisiblecomma/pen/wqLaZQ), but i've done the similar as you can see, but it still doesnt work?
Any ideas out there to help me understand what is going on?
The whole component:
import React, { useState, useEffect } from 'react';
import { Field, reduxForm, getFormValues } from 'redux-form';
import { Col, FormGroup } from 'reactstrap';
import { t } from 'Utilities/i18n';
import Validator from 'Utilities/validation';
import Button from 'Components/Forms/Button';
import { connect } from 'react-redux';
import { gql, useMutation } from '#apollo/client';
import ReactTelInput from 'react-telephone-input';
const formName = 'PhoneVerifyForm';
const performPhoneVerificationMutation = gql`
mutation registerPage_userPhoneVerification($input: RegisterPhoneVerificationInput!) {
userPhoneVerification(input: $input) {
errors {
field
messages
}
phoneNumberVerificationSid
verified
}
}
`;
// TODO props: unconfirmedUserId, phoneNumberVerificationSid, initialValues, callback
const PhoneVerifyForm = (props) => {
const [editPhoneEnabled, setEditPhoneEnabled] = useState(false);
const [codeSend, setCodeSend] = useState(props.phoneNumberVerificationSid !== undefined);
const [performPhoneVerification, { data:performPhoneVerificationData }] = useMutation(performPhoneVerificationMutation);
const [errors, setErrors] = useState([]);
useEffect(() => {
if (performPhoneVerificationData && performPhoneVerificationData.userPhoneVerification) {
const {userPhoneVerification} = performPhoneVerificationData;
if(userPhoneVerification.errors.length === 0) {
setErrors([])
if (editPhoneEnabled) {
editPhoneEnabled(false);
}
setCodeSend(userPhoneVerification.phoneNumberVerificationSid !== undefined);
if(userPhoneVerification.verified !== undefined) {
props.callback(props.formValues.phone);
}
} else {
setErrors(userPhoneVerification.errors)
}
}
}, [performPhoneVerificationData, ])
function handleSubmit(values) {
if (editPhoneEnabled) {
// update phone number
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
phone: values.phoneNumber,
},
},
});
} else if (!codeSend) {
// send new code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
channel: values.channel,
},
},
});
}
// else validate code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
code: values.code,
},
},
});
}
const handleEditPhone = () => {
setEditPhoneEnabled(!editPhoneEnabled);
setCodeSend(false);
};
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
return (
<form className="row" onSubmit={props.handleSubmit(handleSubmit)}>
{!codeSend && <Col style={{background: 'pink'}}>
<p>{t('select channel')}</p>
<FormGroup row className="indented-form-group">
<Col>
<label>
<Field
name="channel"
component="input"
type="radio"
value="sms"
validate={Validator.required}
/>
{t('Sms')}
</label>
</Col>
<Col xs={6}>
<label>
<Field
name="channel"
component="input"
type="radio"
value="phone"
validate={Validator.required}
/>
{t('phone')}
</label>
</Col>
</FormGroup>
</Col>}
{codeSend && <Col style={{background: 'yellow'}}>
<FormGroup row className="indented-form-group">
<Field
labelClassname="required"
label={t('Code')}
name="code"
component="input"
type="text"
validate={[Validator.required]}
/>
</FormGroup>
</Col>}
<Col style={{background: 'red'}}>
<FormGroup row className="indented-form-group">
{!editPhoneEnabled && <div>
<span>PHONE PLACEHOLDER</span><br />
<span onClick={handleEditPhone}>Edit phone number</span>
</div>}
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
</FormGroup>
</Col>
<Col>
<FormGroup row className="indented-form-group">
<Button submit disabled={props.submitting || props.invalid}>
{editPhoneEnabled ? t('Change phone number') : codeSend ? t('Validate code') : t('Send code')}
</Button>
</FormGroup>
</Col>
</form>
);
};
const mapStateToProps = state => ({
formValues: getFormValues(formName)(state) || {}, //This basically gives us the form values in the props and it gets updated on keydown.
});
const decoratedComponent = connect(mapStateToProps, null)(PhoneVerifyForm)
export default (reduxForm({
form: formName,
enableReinitialize: true,
shouldAsyncValidate: ({ trigger }) => ['blur'].includes(trigger),
})(decoratedComponent));

react is editing all array objects instead of just one array object

Currently a user can enter first, lastname, and employee id. When it submits it renders an iteration of employees with the first, last, and employee id.
The problem is when you click edit, it edits the data but it will edit all of the fields for every object in the employees iteration.
How can I fix the code so that it will edit just that particular object within that iteration.
Home component
........
class Login extends Component {
constructor(props) {
super(props);
this.state = {
activeTab: '3',
firstName: '',
lastName: '',
employeeID: '',
employees: [],
Edit: false,
updatedFirst: '',
updatedLast:'',
updatedEmployeeID: ''
};
}
toggle = (tab) => {
if(this.state.activeTab !== tab){
this.setState({
activeTab: tab
})
}
}
onSubmit = (e) => {
e.preventDefault();
const {firstName, lastName, employeeID} = this.state
const ourForm ={
firstName: firstName,
lastName: lastName,
employeeID: employeeID,
// we need an id to so that it can be edited properly
}
this.setState({
employees: [...this.state.employees,ourForm]
}, () => {
console.log(this.state.employees)
})
}
onChange = (e) => {
e.preventDefault()
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value,
});
}
updatedChange = (e) => {
e.preventDefault()
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value,
});
}
onEdit = (e) => {
e.preventDefault();
this.setState({
Edit: !this.state.Edit
})
}
onReset = (e) => {
e.preventDefault();
this.setState({
firstName: '',
lastName: '',
employeeID: ''
})
}
render(){
return (
......
<MyForm
onSubmit={this.onSubmit}
onChange={this.onChange}
onReset={this.onReset}
firstName={this.state.firstName}
lastName={this.state.lastName}
employeeID={this.state.employeeID}
/>
</Col>
</Row>
</TabPane>
</TabContent>
</Container>
<List
employees={this.state.employees}
Edit={this.state.Edit}
onEdit ={this.onEdit}
onChange={this.onChange}
updatedEmployeeID={this.state.updatedEmployeeID}
updatedFirst={this.state.updatedFirst}
updatedLast={this.state.updatedLast}
/>
</div>
);
}
}
export default Login;
Form.js
import React, {Component} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
const MyForm = (props) => {
return(
<Form style={{ margin: '30px 0px'}}>
<FormGroup row>
<Label for="firstName" sm={2} size="sm">First Name:</Label>
<Col sm={10}>
<Input type="text" onChange={props.onChange} value={props.firstName} name="firstName" id="exampleEmail" placeholder="Enter First Name"/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="lastName" sm={2} size="sm">Last Name:</Label>
<Col sm={10}>
<Input type="text" onChange={props.onChange} value={props.lastName} name="lastName" id="exampleEmail2" placeholder="Enter Last Name" />
</Col>
</FormGroup>
<FormGroup row>
<Label for="Employee ID" sm={2} size="sm">Employee ID:</Label>
<Col sm={5}>
<Input type="text" onChange={props.onChange} value={props.employeeID} name="employeeID" id="exampleEmail2" placeholder="Enter Employee ID" />
</Col>
</FormGroup>
<FormGroup row>
<Col sm={12}>
<div className="float-right">
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Add</Button>
<Button onClick={props.onReset} size="lg" style={{ margin: '0px 5px'}}color="warning">Reset</Button>
</div>
</Col>
</FormGroup>
<hr></hr>
<FormGroup row>
<Col sm={4}>
<Input type="text" name="search" id="exampleEmail2" placeholder="Search" />
</Col>
<Col sm={8}>
<Label for="sort" sm={2} size="sm">Sort:</Label>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">First Name</Button>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Last Name</Button>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">ID</Button>
</Col>
</FormGroup>
</Form>
)
}
export default MyForm;
List component
import React, {Component, Fragment} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
const List = (props) => {
return(
<Fragment>
{props.employees.map( (item, i) => (
<div style={{ margin: '40px 0px'}} key={i}>
<hr style={{ border:'1px dashed #000'}}></hr>
<div className="float-right">
<Button onClick={props.onEdit} size="lg" style={{ margin: '0px 5px'}} color="secondary">{props.Edit ? 'Save': 'Edit'}</Button>
<Button size="lg" style={{ margin: '0px 5px'}}color="secondary">Delete</Button>
</div>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedFirst ? props.updatedFirst : item.firstName } name="updatedFirst" placeholder="Enter First Name"/>
):(
<div>
{props.updatedFirst ? props.updatedFirst : item.firstName }
</div>
)}
</Col>
</FormGroup>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID} name="updatedEmployeeID" placeholder="Enter EmployeeID"/>
):(
<div>
{props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID}
</div>
)}
</Col>
</FormGroup>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedLast ? props.updatedLast: item.lastName} name="updatedLast" placeholder="Enter Last Name"/>
):(
<div>
{props.updatedLast ? props.updatedLast : item.lastName}
</div>
)}
</Col>
</FormGroup>
</div>
))}
</Fragment>
)
}
export default List;
The following example shows how to pass a handler and set the state accordingly.
For good measure I separated the logic and the presentation, the presentational components are pure components using React.memo.
//A container should only contain the logic
class EmployeesContainer extends React.Component {
state = {
employees: [{ name: '' }, { name: '' }, { name: '' }],
};
//define what needs to happen if you click edit on an
// employee
onEdit = index => {
//edit will be called with the index of the employee
// the Employees component owns the list of employees
// so it will have to make changes to it
this.setState({
employees: this.state.employees.map((employee, i) =>
i === index ? { ...employee, edit: true } : employee
),
});
};
//Same idea as onEdit, index needs to be passed to indicate
// what employee needs to be changed
onChange = (index, e) => {
this.setState({
employees: this.state.employees.map((employee, i) =>
i === index
? { ...employee, name: e.target.value }
: employee
),
});
};
render() {
return (
<Employees
employees={this.state.employees}
onEdit={this.onEdit}
onChange={this.onChange}
/>
);
}
}
//The Employees presentational component, contains the jsx
// you can make it a pure component by using React.memo
const Employees = React.memo(
({ employees, onEdit, onChange }) => (
<div>
{employees.map((employee, index) => (
<EmployeeContainer
key={index}
index={index}
employee={employee}
onEdit={onEdit}
onChange={onChange}
/>
))}
</div>
)
);
//Make this a container as well because it does more
// than only produce jsx
class EmployeeContainer extends React.Component {
state = {};
//create onChange and onEdit only when index changes
// this will prevent unnecessary renders
static getDerivedStateFromProps(props, state) {
const { index, onChange, onEdit } = props;
if (state.index !== index) {
return {
index,
onChange: e => onChange(index, e),
onEdit: () => onEdit(index),
};
}
return null;
}
render() {
const { employee } = this.props;
const { onChange, onEdit } = this.state;
return (
<Employee
employee={employee}
onChange={onChange}
onEdit={onEdit}
/>
);
}
}
//presentational component, is also pure component
const Employee = React.memo(
({ employee, onChange, onEdit }) => (
<div>
{employee.edit ? (
<input
type="text"
value={employee.name}
onChange={onChange}
/>
) : (
<button onClick={onEdit}>edit</button>
)}
</div>
)
);
I don’t think onSubmit is updating employees correctly. You shouldn’t use this.state inside setState.
this.state inside setState ReactJS
Try this..
this.setState(prevState => ({
employees: [...prevState.employees, ourForm]
}, () => {
console.log(this.state.employees)
}))

TypeError: Cannot convert undefined or null to object React Validation

I have used React validation library
https://www.npmjs.com/package/react-validation
If I visit my page from any other page, it does not give any error. but when I refresh the page, it gives an error
TypeError: Cannot convert undefined or null to object
Please suggest an idea if anybody has faced this issue before.
Code:
import React from 'react';
import {
Card,
CardBody,
CardTitle,
Row,
Col,
FormGroup,
Label,
Alert
} from 'reactstrap';
import Input from 'react-validation/build/input';
import Button from 'react-validation/build/button';
import Form from 'react-validation/build/form';
import axios from 'axios';
import api from '../../config/api';
import messages from '../../config/messages';
import Select from 'react-validation/build/select';
import CKEditor from '#ckeditor/ckeditor5-react';
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
const required = (value, props,components) => {
if(components.formReset!=undefined)
{
if (!components.formReset[0].value && (!value || (props.isCheckable && !props.checked))) {
return <span className="text-danger is-visible">{messages.required}</span>;
}
else if(components.formReset[0].value)
{
return <span className="text-danger is-visible"></span>;
}
}
}
;
class View extends React.Component {
constructor(props) {
super(props);
this.onInputChange = this.onInputChange.bind(this);
this.sendReply = this.sendReply.bind(this);
this.toggle = this.toggle.bind(this);
this.onStatusChange=this.onStatusChange.bind(this);
this.handleEditorChange=this.handleEditorChange.bind(this);
this.state = {
response:null,
loading: false,
message:'',
status:'',
attachment1:[],
status_list:[],
formReset:true
};
}
componentDidMount() {
this.setState({formReset:true});
}
onStatusChange(event) {
this.setState({"formReset":false});
this.setState({
status: event.target.value
});
}
handleEditorChange(data) {
this.setState({"formReset":false});
this.setState({ message:data });
}
sendReply()
{
/*** code after form submission***/
}
toggle() {
}
onInputChange(event) {
event.preventDefault();
this.setState({"formReset":false});
this.setState({
[event.target.name]: event.target.value
});
}
render() {
var _this = this;
return (
<aside className="customizer">
{/*--------------------------------------------------------------------------------*/}
{/* Toggle Customizer From Here */}
{/*--------------------------------------------------------------------------------*/}
<div className="customizer-body pt-3">
<div>
{/* --------------------------------------------------------------------------------*/}
{/* Start Inner Div*/}
{/* --------------------------------------------------------------------------------*/}
<Row>
<Col md="12">
<Card>
<CardTitle className=" border-bottom p-3 mb-0">
</CardTitle>
<CardBody>
<Form ref={c => {
this.replyform = c;
}}>
<Input type="hidden" name="formReset" id="formReset" value={this.state.formReset} />
<FormGroup>
<Row>
<Label sm="2">Reply *</Label>
<Col sm="10">
<CKEditor
editor={ ClassicEditor }
data={this.state.message}
config={ {
toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote' ]
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
this.handleEditorChange(data);
} }
/>
</Col>
</Row>
</FormGroup>
<FormGroup>
<Row>
<Label sm="2">Status</Label>
<Col sm="10">
<Select id="status" className="custom-select form-control" name="status" onChange={this.onStatusChange} value={this.props.status_id: null}>
<option value="">Select</option>
{this.state.status_list.map((status, index) => {
return (
<option key={index} value={status.value}>{status.label}</option>
);
})}
</Select>
</Col>
</Row>
</FormGroup>
<FormGroup>
<Row>
<Label sm="2">Attachments</Label>
<Col sm="10">
<Input
type="file"
id="attachment1" name="attachment1[]"
onChange={this.onInputChange}
multiple="multiple"
/>
</Col>
</Row>
</FormGroup>
<div className="border-top pt-3 mt-3 d-flex no-block">
<Button type="button" onClick={this.sendReply} className="btn btn-dark mr-2 ml-auto">
Send Reply
</Button>
</div>
</Form>
</CardBody>
</Card>
</Col>
</Row>
{/* --------------------------------------------------------------------------------*/}
{/* End Inner Div*/}
{/* --------------------------------------------------------------------------------*/}
</div>
</div>
</aside>
);
}
}
export default View;
Error image:
Really crazy error:
import { Formik, Form, Field } from 'formik';
above required initial values on any cost if you dont want to give initial value initialize it with empty.for example
const initialValues = {
firstname: "",
lastname: ""
}
and then in <Formik tag
initialValues={initialValues}
I was facing the same error using formik
After debugging I came to know I had a typo in the initialValues object name. Make sure you are having the same object for initalValues that you have defined. In my case I had timeCapturesInitialValues object but used timeCapturesInitailValues in the Formik prop.
after upgrading react & it's packages i was facing above issue & it's resolved by Hassan Ali Shahzad answer,
code that may help some one
<Formik
enableReinitialize={true}
initialValues={{
heatmaps: '',
}}
if your return statement is null at any given point this error occurs.
Example:
const obj = null;
return(
obj
);
Please check your code .

react-bootstrap - login form - Uncaught TypeError

I have the following login.js
import React, { Component, PropTypes } from 'react';
import { Button, Form, FormControl, FormGroup } from 'react-bootstrap';
export default class Login extends Component {
render() {
const {errorMessage} = this.props
return (
<Form inline>
<FormGroup controlId="formInlineEmail">
<FormControl type="email" ref="username" placeholder="Email"/>
</FormGroup>
<FormGroup controlId="formInlinePassword">
<FormControl type="password" ref="password"/>
</FormGroup>
<Button type="submit" onClick={(event) => this.handleClick(event)}>
Login
</Button>
{errorMessage &&
<p style={{color:'red'}}>{errorMessage}</p>
}
</Form>
)
}
handleClick(event) {
const username = this.refs.username
const password = this.refs.password
const creds = { username: username.value.trim(), password: password.value.trim() }
this.props.onLoginClick(creds)
}
}
Login.propTypes = {
onLoginClick: PropTypes.func.isRequired,
errorMessage: PropTypes.string
}
When attempting to login I get the following error:
Uncaught TypeError: (0, _reactDom2.default) is not a function at Login.handleClick
if I avoid using react-bootstrap and do the following in my Login.js it works fine.
<div>
<input type='text' ref='username' className="form-control" style={{ marginRight: '5px' }} placeholder='Username'/>
<input type='password' ref='password' className="form-control" style={{ marginRight: '5px' }} placeholder='Password'/>
<button onClick={(event) => this.handleClick(event)} className="btn btn-primary">
Login
</button>
{errorMessage &&
<p style={{color:'red'}}>{errorMessage}</p>
}
</div>
Don't ask why but this works:
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import { Button, ControlLabel, Form, FormControl, FormGroup } from 'react-bootstrap';
export default class Login extends Component {
handleSubmit(event) {
const username = findDOMNode(this.refs.username)
const password = findDOMNode(this.refs.password)
const creds = { username: username.value.trim(), password: password.value.trim() }
this.props.onLoginClick(creds)
}
render() {
const {errorMessage} = this.props
return (
<Form inline>
<FormGroup controlId="formHorizontalEmail">
<ControlLabel>Email </ControlLabel>
<FormControl type="username" ref="username" onChange={this.handleChange} placeholder="Email" />
</FormGroup>
<FormGroup controlId="formHorizontalPassword">
<ControlLabel>Password </ControlLabel>
<FormControl type="password" ref="password" onChange={this.handleChange} placeholder="Password" />
</FormGroup>
<Button onClick={(event) => this.handleSubmit(event)}>Login</Button>
{errorMessage &&
<p style={{color:'red'}}>{errorMessage}</p>
}
</Form>
)
}
}
Login.propTypes = {
onLoginClick: PropTypes.func.isRequired,
errorMessage: PropTypes.string
}

Resources