I'm new to React. I've a MealList component to which I'm passing a set of props, based on which it make a data call and updates an array of meals, which I display in a table.
const MealList = (props) => {
const [meals, setMeals] = useState([]);
useEffect(() => {
const fetchMeals = async (userId, fromDate, toDate, fromTime, toTime) => {
...
return resp;
};
fetchMeals(localStorage.getItem('user_id'), props.fromDate, props.toDate, props.fromTime, props.toTime).then(r => {
setMeals([...meals, ...r.data])//The fetched data is stored in an array here.
});
}, [props]);
console.log(props.fromDate);
return (
<div style={{width: '70%'}}>
...
<Table striped bordered hover>
<thead>
<tr>
...
</tr>
</thead>
<tbody>
{meals.map((meal, index) => (<Meal key={meal.id} count={index +1} meal={meal}/>))}//And displayed here
</tbody>
</Table>
</div>
)
};
The problem I'm facing is that using the spread syntax setMeals([...meals, ...r.data])
appends to the existing list everytime MealList is updated via the props.
My question is how can I set the meals array back to null and then update only the new values? I've tried this:
fetchMeals(localStorage.getItem('user_id'), props.fromDate, props.toDate, props.fromTime, props.toTime).then(r => {
setMeals([]);
setMeals([...meals, ...r.data])
});
But this doesn't work either.
If you want the same effect of the splicing you should use
setMeals(r.data.slice());
as otherwise the reference to r.data
is passed and it can make a difference if that object is mutated after the setMeals
call.
When you pass an array to a function in Javascript the function doesn't receive a copy of the array, but a reference to the original object you passed. For example:
let x = [1,2,3], y = null;
function foo(xx) {
y = xx;
}
foo(x);
console.log(y); // gives [1, 2, 3]
x[1] = 99;
console.log(y); // gives [1, 99, 3]
Apparently the code in fetchMeals
(that we don't see) reuses the r.data
array and this creates the problem if you don't make a copy. I would probably classify this as a (design) bug in fetchMeals
as given the interface I'd expect to get a fresh answer and not a reused one that I must copy.
Note also that
x.slice()
is the same as
[...x]
Here is an example (trying to mimic your code):
Stackblitz demo: https://stackblitz.com/edit/react-hooks-usestate-svnmpn?file=MealList.js
The problem is with data mutation, you have to remain immutable if you want new changes to re-render you component and apply the update.
const MealList = props => {
const [meals, setMeals] = useState([]);
useEffect(() => {
const fetchMeals = async (userId, fromDate, toDate, fromTime, toTime) => {
return await new Promise(res =>
setTimeout(
_ =>
res({
data: [
{ id: 1, name: "sphagetti" },
{ id: 2, name: "salad" },
{ id: 3, name: "soup" },
{ id: 4, name: "bacon and eggs" }
]
}),
2000
)
);
};
fetchMeals(1, "date1", "date2", "time", "time2").then(r =>
setMeals([...r.data])
);
}, [props]);
return (
<div style={{ width: "70%" }}>
{!meals.length && <p>wait 2 seconds...</p>}
{meals.map((meal, index) => (
<div key={meal.id} count={index + 1} meal={meal}>
{meal.id + ". " + meal.name}
</div>
))}
</div>
);
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With