So I'm running into a weird issue with my SPA regarding states.
I have a left side menu with items, let's say organization and when I click on any of them, the right hand panel changes with the appropriate list of users within the selected organization.
For example: I have organization 1 and organization 2 in the list, and if I click on organization 1, I send a request to middleware to retrieve the list of users within that organization and if I select organization 2, I do that same thing.
So I have a higher component Organization.js that has the following code:-
// Organization.js
const [selectedOrganization, setSelectedOrganization] = useState(null);
// This method will be called when we select an organization from the menu
const handleSelectedOrganization = organization => {
if (!selectedOrganization || selectedOrganization.id !== organization.id) {
setSelectedOrganization(organization);
}
};
return (
<UsersView selectedOrganization={selectedOrganization} />
);
UsersView.js
const UsersView = ({ selectedOrganization = {} }) => {
const [selectedOrganizationUsers, setSelectedOrganizationUsers] = useState(
[]);
let globalOrganization = selectedOrganization?.id; // global var
const refreshOrganizationsList = () => {
const localOrganization = selectedOrganization.id; // local var
Promise.all([ // bunch of requests here]).then(data => {
console.log('from global var', globalOrganization); // outdated
console.log('from local var', localOrganization); // outdated
console.log('from prop', selectedOrganization.id); // outdated
setSelectedOrganizationUsers(data.result); // data.result is an array of objects
});
};
// Will be called when the selectedOrganization prop changes, basically when I select
//a new organization from the menu, the higher component will
// change state that will reflect here since the prop will change.
useEffect(() => {
if (selectedOrganization) {
globalOrganization = selectedOrganization.id;
refreshOrganizationsList();
}
}, [selectedOrganization]);
console.log(selectedOrganization?.id); // Always updated *1
return (
{selectedOrganizationUsers?.length ? (
<>
<div className="headers">
....
)
Now the problem is, some API calls take too long to respond, and in a particular scenario when I switch between orgs fast, we would get some pending API calls and when the response comes, the states are messed up.
For example: If I select from the menu Organization 1, we send 3 requests to middleware that would remain pending let's say for 10 seconds.
If after 5 seconds, I choose Organization 2 from the menu where its API requests would be instant, the right hand panel will be updated with the Organization 2 data but then after 5 seconds, when Organization 1 requests get the responses, the list gets updated with Organization 1 data which is what I try to prevent since now we have selected organization 2.
The reason why I have console.logs in the .then() is because I try to block updating the states when the current selectedOrganization !== the organization.id in the response.
But unfortunately, the console.logs in the above scenario would should me the organization id = 1 and not 2, even if I have selected organization 2 already.
For example:
I select Organization 1, then I selected Organization 2
once I select Organization 2, the outside *1 console.log would log 2 immediately in my browser.
But when I get the API responses of 1, the console.logs inside the .then() gives me 1 not 2, I expect them to give me 2 so that I can make an if (request.organization_id !== selectedOrganization.id) -> don't update the states
Long story short, it seems that when the API call returns with a result, the organization.id within the .then() is always the one was had when we fired the request itself and not the most updated part. As if it's no longer tied with the recent value within the props that comes from the state of the higher component