Here's the template for a form I'm writing with Formik and react-bootstrap. I'm finding a very strange error: if I initialise my state with dummy data in the constructor, it works fine; but if I call setState with the exact same data in componentDidMount
to simulate an API call, it breaks horribly.
Specifically, I find that the state variable alertVehicles
array can have non-zero length, but the corresponding Formik values.alertVehicles
variable can be empty. Right now, the form as written renders no checkboxes. If I use alertVehicles
instead of values.alertVehicles
in my guard clause, then it blows up with an error Cannot read property 'selected' of undefined
.
import React, {Component} from 'react';
import Form from 'react-bootstrap/Form';
import { Formik } from 'formik';
import Button from 'react-bootstrap/Button';
class Alerts extends Component {
constructor(props) {
super(props);
this.loadAlertData = this.loadAlertData.bind(this);
this.state = {
alertRecipient: {},
alertId: '',
alertVehicles: []
}
}
componentDidMount(){
this.loadAlertData();
}
loadAlertData(){
// this will be an API call eventually, obviously.
// if this is initialised in the constructor then everything works!
this.setState( {
alertRecipient: {
name: 'Rob',
simNumber: '0123456789',
},
alertId: 1,
alertVehicles: [
{id: 1, vrn: 'vehicle A', selected: true },
{id: 2, vrn: 'vehicle B', selected: false },
{id: 3, vrn: 'vehicle C', selected: true }
]
})
}
render() {
const { alertRecipient, alertId, alertVehicles } = this.state;
return (
<>
<Formik
initialValues={{ alertRecipient, alertId, alertVehicles }}
onSubmit={ values => {
window.alert(JSON.stringify(values))
}
}
render={({values, handleChange, handleSubmit}) => (
<Form onSubmit={handleSubmit}>
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
name="alertRecipient.name"
value={values.alertRecipient.name}
onChange={handleChange}
/>
<Form.Label>Phone number</Form.Label>
<Form.Control
type="text"
name="alertRecipient.simNumber"
value={values.alertRecipient.simNumber}
onChange={handleChange}
>
</Form.Control>
<Form.Label>Vehicles</Form.Label>
{
//get an error if we just use alertVehicles.length here??
values.alertVehicles.length === 0 ? null : alertVehicles.map((veh, index) => (
<Form.Check type="checkbox"
key={veh.id}
label={veh.vrn}
name={`alertVehicles[${index}].selected`}
checked={values.alertVehicles[index].selected}
onChange={ handleChange }
/>
))
}
<Button type="submit">Save</Button>
</Form>
)
}
/>
</>
)
}
}
export default Alerts;
I don't understand
componentDidMount
values.alertVehicles
doesn't appear to be in sync with alertVehicles
.Thanks in advance for any help.
For some reason this is Formik's default behaviour, and you need to supply the enableReinitialize
prop to override it:
https://github.com/jaredpalmer/formik/issues/811