Reason
The code is not working due to improper usage of useState
hook. useState
is asynchronous in nature. Due to this, the variables are not updated with proper values when you are using it. The current code is assuming variables to be updated in synchronous manner.
setPermission(perm)
is done in one line and permission
variable is used in next line. But permission variable won't be updated at that time.
- Same seems for
admin
variable.
That's why for the first time, the code doesn't work (the variables are not updated). But when you click again, the older values are picked; condition is satisfied; making it work.
Read more about it here.
Fix
There can be two ways in which this code can be fixed.
Approach 1
In the if condition, you can use the function arguments directly instead of variable from use state.
The relevant fixed code will be:
const UserCard = (props) => {
...
const adminUpdate = (perm, id) => {
if(!perm || !id){ // Change this condition here
return
}else{
api.UserManagement.put(
id,
{permissions: [perm]} // Change here as well
).catch(err => {
console.log("error", err, err.status)
})
}
}
return (
<>
...
</DropdownToggle>
<DropdownMenu onClick={() => setAdminID(item.id)} key={item.id}>
// Pass id onClick of DropdownItem also
<DropdownItem value={"Admin"} onClick={e => adminUpdate(e.target.value, item.id)}>Admin</DropdownItem>
<DropdownItem value={"Sales"} onClick={e => adminUpdate(e.target.value, item.id)}>Sales</DropdownItem>
<DropdownItem value={"Medical"} onClick={e => adminUpdate(e.target.value, item.id)}>Medical</DropdownItem>
</DropdownMenu>
</Dropdown>
...
</>
);
};
This approach almost removes the usage of useState
hook as you are directly using variables passed from function
Approach 2
You can also use useEffect
hook along with useState
. useEffect takes dependency array as second argument and will trigger function call when any of the variable is changed. So, the logic will be splitted into 2 parts:
- Variables will be updated separately by useState
- Api call will be triggered when any of variable is updated in useEffect.
The relevant fixed code will be:
const UserCard = (props) => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [permission, setPermission] = useState()
const [adminID, setAdminID] = useState()
const item = props.item;
const toggle = () => {
setDropdownOpen(prevState => !prevState)
};
useEffect(() => {
if (permission && adminID) {
api.UserManagement.put(
adminID,
{permissions: [permission]}
).catch(err => {
console.log("error", err, err.status)
})
}
}, [permission, adminID]) // This will trigger the function call when any of these 2 variables are modified.
return (
<>
...
</DropdownToggle>
// setAdminID on click
<DropdownMenu onClick={() => setAdminID(item.id)} key={item.id}>
// setPermission onClick
<DropdownItem value={"Admin"} onClick={e => setPermission(e.target.value)}>Admin</DropdownItem>
<DropdownItem value={"Sales"} onClick={e => setPermission(e.target.value)}>Sales</DropdownItem>
<DropdownItem value={"Medical"} onClick={e => setPermission(e.target.value)}>Medical</DropdownItem>
</DropdownMenu>
</Dropdown>
</td>
</tr>
</>
);
};
Approach 1 vs Approach 2
It's upto you which approach to chose from. Both will get you the result but differ in base approach. Approach 1 is triggering the API call directly on click of dropdown item but Approach 2 is updating the variables on click of dropdown items and the change of those variables is triggering the API call.
Hope it helps. Revert for any doubts/clarifications.