I'm working on some sign in code, and want to add the functionality to check whether the user is an admin or not. I know that I can't call a hook within a callback, and I've checked other answers on SO, but still can't wrap my head around how to use the hook's functionality. The problem is it has to be triggered from within a form's submit function - so I can't call it at the top level of my React component, as the error message suggests. (React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks)
Can anyone help me figure out how to refactor this code to get the hook's functionality into a callback?
Description of code:
SignIn component - is a form with an onSubmit function, which calls a custom signIn function.
signIn function - takes user/pass and a callback function to be called only if the Auth flow succeeds.
hook useCheckUserAdmin - check if user is an admin. Ideally called inside signIn's callback.
onSuccess function - redirects to either admin or client homepage. called inside signIn's callback, right after the hook.
Actual Code:
Here's the main Login component (note some irrelevant pieces are removed), including onSuccess function
const SignInComponent = () => {
const onSuccess = React.useCallback(
(userIsAdmin) => {
// history.replace(etc. etc.) <- some boolean logic to direct to an admin or a regular user page
},
[]
);
return <Formik
onSubmit={async (values) => {
await signIn(values.username, values.password, (userId) => {
const userIsAdmin = useCheckUserAdmin(userId)
onSuccess(userIsAdmin);
setError(null);
}).catch((err) => {
setError(err.message);
});
}}
>
}
The custom hook useCheckUserAdmin (simplified for readability):
import { useGetUser } from "../api/composedHooks";
export default function useCheckUserAdmin(userId: string) {
const userQuery = useGetUser({
variables: { id: userId },
});
return userQuery.userType;
}
The signIn function:
async function signIn(
username: string,
password: string,
callback: (userId: string) => void
) {
try {
const result = await Auth.signIn({
username,
password,
});
if (result) {
return callback(result.username);
}
} catch (error) {
console.error("error signing in:", error);
throw error;
}
}
There's really no way to call the custom hook in your callback (since it fails the rules of hooks check). If you must perform this logic inside of a custom hook (which may not be necessary since i cant see why that requires stateful logic), one option would be to return the necessary function from the custom hook:
SignInComponent = () => {
const checkUserIsAdmin = useCheckUserIsAdmin() // non-conditionally call the hook
}
Then in your callback something like:
callback(checkUserIsAdmin)
And your custom hook you call any needed hooks & return the function:
// Call any needed hooks (useLocation..useHistory..etc)
const checkUserIsAdmin = () => {...logic operating on userId}
return checkUserIsAdmin
Not sure if it helps but it's one way to satisfy the conditions of your problem.