Reputation: 45
I have a list with opening_hours that I map inside my render function. I map them to a child component. I want to change the value of each object inside my parent state when the child component state is changed. Only problem is that my values don't change in the child component nor the parent state. I have passed the onChange method to each child component, but the problem lies in my onChange function in the parent state.
I have tried to pass the onChange to my child component and update the state with [e.target.name] : e.target.value. But that didn't work. So I tried to copy the arrayList and update the state based on the index of the mapped arrayList. That also didn't work.
handleOnChange = (index) => {
let newData = [...this.state.opening_hours];
this.setState(
newData.map((barbershop, j) => {
if (j === index) {
return barbershop;
console.log(barbershop)
}
return {
newData,
};
}));
console.log(newData)
};
render() {
return (
<>
<div className="container mb-2">
<h4>Opening hours </h4>
<Form>
{this.state.opening_hours.map((barbershop, index) => (
<Day
key={barbershop.id}
day={barbershop.day}
open_time={barbershop.open_time}
close_time={barbershop.close_time}
index = {index}
onChange={() => this.handleOnChange(index)}/>
))}
</Form>
<Button onClick={this.handleSubmit} variant="primary" type="submit">
Update opening hours
</Button>
</div>
</>
);
}
}
My child component looks like this
class Day extends Component {
constructor(props) {
super(props);
this.state = {
day: this.props.day,
open_time: this.props.open_time,
close_time: this.props.close_time,
};
console.log(this.state)
}
handleChange = () => {
this.props.onChange(this.props.index)
}
render() {
return (
<Form.Group as={Row}>
<Form.Label column sm="3">
{ENUM[this.state.day]}
</Form.Label>
<Col sm="3">
<Form.Control
name="open_time"
value={this.state.open_time}
onChange={this.handleChange}
type="time"
min="08:00"
max="24:00"/>
</Col>
<Col sm="3">
<Form.Control
name="close_time"
value={this.state.close_time}
onChange={this.handleChange}
type="time"
min="08:00"
max="24:00"/>
</Col>
</Form.Group>
);
}
}
EDIT:
Upvotes: 2
Views: 5026
Reputation: 968
So I have done some investigation, I believe the problem is that you are not returning data back to the consumer.
Here is what I think, the solution is:
import React, { Component, useState, useEffect } from "react";
class UI extends Component {
handleOnChange = ({ index, ...newHours }) => {
// update array item
const opening_hours = this.state.opening_hours.map((item, i) => {
const newData = i === index ? newHours : {};
return {
...item,
...newData
};
});
// update item in opening_hours => state
this.setState({ opening_hours });
};
render() {
return (
<div className="container mb-2">
<h4>Opening hours</h4>
<Form>
{this.state.opening_hours.map(({ id, ...barbershop }, index) => (
<Day
key={id}
{...barbershop}
{...{ index }}
onChange={this.handleOnChange}
/>
))}
</Form>
<Button onClick={this.handleSubmit} variant="primary" type="submit">
Update opening hours
</Button>
</div>
);
}
}
const Day = ({ day, onChange, index, ...props }) => {
const [open_time, setOpenTime] = useState(props.open_time);
const [close_time, setCloseTime] = useState(props.close_time);
useEffect(() => {
onChange({ index, open_time, close_time });
}, [open_time, close_time, onChange, index]);
const sharedProps = {
type: "time",
min: "08:00",
max: "24:00"
};
return (
<Form.Group as={Row}>
<Form.Label column sm="3">
{ENUM[day]}
</Form.Label>
<Col sm="3">
<Form.Control
{...sharedProps}
name="open_time"
value={open_time}
onChange={({ target }) => setOpenTime(target.value)}
/>
</Col>
<Col sm="3">
<Form.Control
{...sharedProps}
name="close_time"
value={close_time}
onChange={({ target }) => setCloseTime(target.value)}
/>
</Col>
</Form.Group>
);
};
Upvotes: 2
Reputation: 175
You're passing a function that calls this.handleChange(index)
, but you should really be passing that method as a prop to your <Day />
component and call it in there. You're already passing in the index
(although I would use barbershop.id
instead)
So in your parent component, I would do this instead:
<Day
key={barbershop.id}
day={barbershop.day}
open_time={barbershop.open_time}
close_time={barbershop.close_time}
index = {barbershop.id}
handleChange={this.handleOnChange}
/>
Then in your this.handleChange
method in the parent component, if you're just trying to get this.state.opening_hours
for a specific barbershop, I would do something like this instead:
handleChange = (id) => {
this.setState(() => ({
opening_hours: this.state.opening_hours.filter((barbershop) => {
return barbershop.id === id;
})
}))
}
Upvotes: 1