I attempt to make auth flow in Context Provider and have troubles with TypeScript. Here is problem fragment of Provider code:
interface AuthUserProviderProps {
children?: React.ReactNode
}
interface AuthUserInterface {
token: string;
setToken: (value: string) => void;
loggedIn: boolean;
};
setBrand: (value: {}) => void;
}
export const authUser = createContext<AuthUserInterface | null >(null);
const AuthUserProvider = ({ children }: AuthUserProviderProps) => {
if (accessTokenVAR() && !jwtToken?.roles.toString().includes('admin')) {
return isBrowser ? window.location.replace('www.somewhere.com') : ''
}
return (
<authUser.Provider
value={{
token,
setToken,
loggedIn,
}}
>
{ children}
</authUser.Provider>
);
};
export default AuthUserProvider;
App.tsx
export default function App({ Component, pageProps }: AppProps) {
const apolloClient = useApollo(pageProps.initialApolloState);
return (
<ThemeProvider theme={TifTheme}>
<AuthUserProvider> <===This line throws error
<ApolloProvider client={apolloClient} >
<CssBaseline />
<Component {...pageProps} />
</ApolloProvider>
</AuthUserProvider>
</ThemeProvider>
);
}
The error is 'AuthUserProvider' cannot be used as a JSX component. Its return type 'void | "" | Element' is not a valid JSX element. Type 'void' is not assignable to type 'Element.' and it is caused by return redirect. But I have no clue how to handle this error
Your analysis should be correct, it's the return of the "redirect". You can easily type properties of React functional components with React.FunctionComponent
.
I would recommend to write a little useAccess hook and use that to get a boolean for display reasons or redirect the user with a useEffect. As you redirect to a different page, it shouldn't matter what the component returns. So I modified your code to this, I made some changes and added comments, let me know if it helps.
import { createContext, FunctionComponent, useEffect, useMemo } from "react";
interface AuthUserInterface {
token: string;
setToken: (value: string) => void;
loggedIn: boolean;
setBrand: (value: any) => void;
}
const AuthUser = createContext<AuthUserInterface | null>(null);
const useAccess = (isBrowser: boolean) => {
const hasAccess = useMemo(
() => accessTokenVAR() && !jwtToken?.roles.toString().includes("admin"),
// TODO: check if that's correct, these variables were not in your answer,
// might need to add/pass them to the hook
[accessTokenVAR, jwtToken]
);
useEffect(() => {
// redirect here if has
if (hasAccess && isBrowser) window.location.replace("www.somewhere.com");
}, [isBrowser, hasAccess]);
return hasAccess;
};
interface AuthUserProviderProps {
isBrowser: boolean;
}
const AuthUserProvider: FunctionComponent<AuthUserProviderProps> = ({
children,
isBrowser
}) => {
const hasAccess = useAccess(isBrowser);
return (
<>
{!hasAccess && <p>"No access"</p>}
{hasAccess && (
<AuthUser.Provider
value={{
// TODO: values not in question, where do they come from
token,
setToken,
loggedIn
}}
>
{children}
</AuthUser.Provider>
)}
</>
);
};
export { AuthUserProvider as default, AuthUser };