Reputation: 137
I have some troubles with updating state in my component: i got few services which can be selected and selection, starts async loading this service, so i want to show some loading state, but when there is more than one service and i select both ( pretty fast ) i'm getting wrong previous state
when i'm fast click on first and second service, their both need to be loading but actual loading state when i click on second service === {0: false, 1: true} must be {0: true, 1: true} but i can't figure out why first service loading === false
there is code
generating initial state
const [loadingState, setLoadingState] = React.useState(
availableServices.reduce((loadingState, service) => {
loadingState[service.id] = false;
return loadingState;
}, {})
);
actual state
useEffect(() => {
console.log('actual loading state', loadingState);
}, [Object.keys(loadingState).map(id => id)]);
handleSelect function
const onSelect = useCallback(
async (id: string) => {
const selectedServicesIds = selectedServices.map(service => service.id);
setAddingInProgress(true);
console.log('set to true', loadingState);
setLoadingState({ ...loadingState, ...{ [id]: true } });
// set to true {0: false, 1: false}
// actual loading state {0: true, 1: false}
// click on second service
// set to true {0: false, 1: false}
// actual loading state {0: false, 1: true} <-- there is need to be {0: true, 1: true}
if (selectedServicesIds.includes(id)) {
selectedServicesIds.splice(selectedServicesIds.findIndex(serviceId => serviceId === id), 1);
await updateServices(selectedServicesIds);
} else {
selectedServicesIds.push(id);
await updateServices(selectedServicesIds);
}
setAddingInProgress(false);
console.log('set to false', loadingState);
setLoadingState({ ...loadingState, ...{ [id]: false } });
},
[selectedServices]
);
Upvotes: 0
Views: 325
Reputation: 3046
Since there is no question to answer I will just review real quick:
map
in [Object.keys(loadingState).map(id => id)]
always creates a new object so your effect will be performed on each render which is not desireableUpvotes: 0
Reputation: 1681
I think this will work
const onSelect = useCallback(
async (id: string) => {
const selectedServicesIds = selectedServices.map(service => service.id);
setAddingInProgress(true);
console.log('set to true', loadingState);
setLoadingState(prev => ({ ...prev, ...{ [id]: true } }));
// set to true {0: false, 1: false}
// actual loading state {0: true, 1: false}
// click on second service
// set to true {0: false, 1: false}
// actual loading state {0: false, 1: true} <-- there is need to be {0: true, 1: true}
if (selectedServicesIds.includes(id)) {
selectedServicesIds.splice(selectedServicesIds.findIndex(serviceId => serviceId === id), 1);
await updateServices(selectedServicesIds);
} else {
selectedServicesIds.push(id);
await updateServices(selectedServicesIds);
}
setAddingInProgress(false);
console.log('set to false', loadingState);
setLoadingState(prev => ({ ...prev, ...{ [id]: false } }));
},
[selectedServices]
);
Upvotes: 2