Reputation: 255
So my goal is to have one react component that essentially lets you have a few of a child component if you press a button. I want to manage the state in the top-level component, and when you hit submit, I want the state of this component to essentially be an array of objects, one element for each component in the list. My idea for a solution was to render a couple of the child components and pass them an index. Then, I give them the upper-level onChange
function for them to call with the index. The problem with this is that it works until I call this.setState
in the upper-level onChange
function. For some reason, calling this.setState
causes my index
parameter to always be the highest number index in the list of child elements.
Here is AddressesDataForm.js:
export default class AddressesDataForm extends React.Component {
constructor(props) {
super(props);
if (this.props.onChange) {
functions.onChange = this.onChange;
}
if(this.props.onSubmit) {
functions.onSubmit = this.props.onSubmit;
}
this.state = {
'tableName': 'Addresses',
states: [
'Gotta',
'Hook',
'Into',
'State',
'Api',
'Still'
],
county_disabled: true,
counties: {
'Gotta': ['a', 'b', 'c'],
'Hook': ['d', 'e', 'f'],
'Into': ['e', 't', 'c'],
'State': ['e', 't', 'c'],
'Api': ['e', 't', 'c'],
'Still': ['e', 't', 'c']
},
use_counties: [],
countries: [
'United States of America',
'Elsewhere'
],
index: this.props.index
}
console.log(this.props.index);
}
onChange = (name, value) => {
if (name == 'state') {
if (value.length > 0) {
this.setState({
county_disabled: false,
use_counties: this.state.counties[value]
});
} else {
this.setState({
county_disabled: true
});
}
}
console.log(this.props.index); //notice this.props.index, tried this.state as well and it didn't work either
this.props.onChange(name, value, this.props.index);
}
render() {
return (
<DataForm tableName={this.state.tableName} onSubmit={functions.onSubmit.bind(this)} {...this.props}>
<SelectField
menuItems={['Home', 'Work', 'Other']}
defaultValue="Home"
className='md-cell md-cell--2 md-cell--middle'
onChange={functions.onChange.bind(this, "address_type_select")}
/>
<TextField
label="Street Address Line 1"
maxLength={128}
className='md-cell md-cell--10'
onChange={functions.onChange.bind(this, "address_line_1")}
/>
<TextField
label="Street Address Line 2"
maxLength={128}
className='md-cell md-cell--2-desktop-offset md-cell--10'
onChange={functions.onChange.bind(this, "address_line_2")}
/>
<TextField
label="City"
maxLength={64}
className='md-cell md-cell--2-desktop-offset md-cell--5'
onChange={functions.onChange.bind(this, "city")}
/>
<SelectField
label="State"
maxLength={64}
className='md-cell md-cell--2'
menuItems={this.state.states}
onChange={this.onChange.bind(this, "state")}
/>
<TextField
label="ZIP code"
className='md-cell md-cell--3'
onChange={functions.onChange.bind(this, "zip_code")}
required
/>
<SelectField
label="Country"
className='md-cell md-cell--2-desktop-offset md-cell--10'
menuItems={this.state.countries}
defaultValue={this.state.countries[0]}
onChange={functions.onChange.bind(this, "country")}
/>
<h6 className="md-cell md-cell--2-desktop-offset md-cell--10">Location Information</h6>
<SelectField
label="County"
className='md-cell md-cell--2-desktop-offset md-cell--10'
menuItems={this.state.use_counties}
onChange={this.onChange.bind(this, "county")}
helpText={this.state.county_disabled ? 'Select a state' : ''}
disabled={this.state.county_disabled}
/>
<SelectionControl
id={"switch-primary-address" + this.props.index}
className='md-cell md-cell--12 md-cell--2-desktop-offset'
type="switch"
label="Primary address"
onChange={functions.onChange.bind(this, "primary_address")}
checked={this.props.isPrimary}
/>
{this.props.noAddButton ?
null :
<div className="md-cell">
<Button
style={{ display: "inline-block" }}
floating mini secondary
onClick={this.props.onAddClicked}>
add circle
</Button>
<p style={{ display: "inline-block", margin: "10px" }}>Add address</p>
</div>}
{this.props.noButton ? null : <h6 className="md-cell md-cell--12">* Required Fields</h6>}
<p>{this.props.index}</p>
</DataForm>
);
}
}
and AddressesDeck.js
export default class AddressesDeck extends React.Component {
constructor(props) {
super(props);
this.state = {
addresses: 3,
primaryAddress: 0,
data: {}
};
}
onAddClicked = () => {
this.setState({
addresses: this.state.addresses + 1,
primaryAddress: this.state.addresses
});
}
onChange = (name, value, index) => {
console.log(index); //properly logs the index
// this.setState(prevState => ({ //unless this is un-commented, then it always logs 2
// 'something unrelated':value
// }));
console.log(this.state);
// if (name == 'primary_address') {
// this.setState({
// primaryAddress: index
// });
// }
}
render() {
return (
// <div>
// {[...new Array(this.state.addresses)].map((_, i) => (
// <Paper id="main" key={i}>
// <AddressesDataForm
// onChange={this.onChange.bind(this, i)}
// onSubmit={functions.onSubmit.bind(this)}
// key={i}
// index={i}
// onAddClicked={this.onAddClicked.bind(this)}
// noButton={i != this.state.addresses - 1}
// noAddButton={i != this.state.addresses - 1 || this.state.addresses == 3}
// noTitle={i != 0}
// isPrimary={this.state.primaryAddress == i} />
// </Paper>
// ))}
// </div>
<div>
<Paper id="main" key={0}>
<AddressesDataForm
onChange={this.onChange}
onSubmit={functions.onSubmit.bind(this)}
key={0}
index={0}
onAddClicked={this.onAddClicked.bind(this)}
noButton={0 != this.state.addresses - 1}
noAddButton={0 != this.state.addresses - 1 || this.state.addresses == 3}
noTitle={0 != 0}
isPrimary={this.state.primaryAddress == 0} />
</Paper>
<Paper id="main" key={1}>
<AddressesDataForm
onChange={this.onChange}
onSubmit={functions.onSubmit.bind(this)}
key={1}
index={1}
onAddClicked={this.onAddClicked.bind(this)}
noButton={1 != this.state.addresses - 1}
noAddButton={1 != this.state.addresses - 1 || this.state.addresses == 3}
noTitle={1 != 0}
isPrimary={this.state.primaryAddress == 1} />
</Paper>
<Paper id="main" key={2}>
<AddressesDataForm
onChange={this.onChange}
onSubmit={functions.onSubmit.bind(this)}
key={2}
index={2}
onAddClicked={this.onAddClicked.bind(this)}
noButton={2 != this.state.addresses - 1}
noAddButton={2 != this.state.addresses - 1 || this.state.addresses == 3}
noTitle={2 != 0}
isPrimary={this.state.primaryAddress == 2} />
</Paper>
</div>
);
}
}
Upvotes: 0
Views: 124
Reputation: 15106
This probably happens because in the constructor, you assign this.onChange
to a functions
object, which is shared between all instances of your AddressesDataForm
component. When rendering each instance the first time, the constructor was just called, and functions.onChange
will have the correct value. However, when setting the parent's state and rerendering each instance, no constructor is called, and functions.onChange
in each of the three renderings will refer to the third this.onChange
(which calls this.props.onChange
with an index of 2.)
It's a little hard to see without running the code, but I would guess that simply using this.onChange
would fix the problem. Also, to prevent confusion, you could rename the prop passed down to AddressesDataForm
to something other than onchange
.
Upvotes: 1