Issue
I am using React Router 6.4.3 with the new data routers.
A 3rd party authentication library exposes an authentication context and a login function through a hook, like:
const { AuthenticationContext, login } = useAuthentication();
I already managed to wrap the routes into the AuthenticationContext
by using a pathless route in createBrowserRouter
.
However, I fail to understand how I could make use of route actions to perform the login and redirect the user afterwards. As configuration and action are now outside of a functional component, I cannot make use of the useAuthentication
hook.
My configuration looks like this:
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { AuthProvider } from 'auth';
import Login, { action as loginAction } from './routes/login';
const router = createBrowserRouter([
{
element: <AuthProvider />,
children: [
{
path: '/login',
element: <Login />,
action: loginAction,
},
],
},
]);
function App() {
return (
<RouterProvider router={router} />
);
}
With loginAction
currently only a stub:
const action = async ({ request, params }) => {
const formData = await request.formData();
const updates = Object.fromEntries(formData);
// How to invoke login() method exposed by useAuthentication() hook here?
}
The AuthenticationContext
and associated methods roughly look like this:
const loginApi = async (email, password) => {
// ajax call to login endpoint
}
const AuthContext = React.createContext(null);
const AuthProvider = () => {
const [jwt, setJwt] = useState(null);
const navigate = useNavigate();
const login = async (email, password) => {
const jwt = await loginApi(email, password);
setJwt(jwt);
navigate('/dashboard');
}
const contextValue = {
jwt,
login: handleLogin,
}
return (
<AuthContext.Provider value={contextValue}>
<Outlet />
</AuthContext.Provider>
);
}
const useAuth = () => {
return useContext(AuthContext);
}
export default AuthProvider;
export { useAuth };
Is there a way to make this work with the data routers without touching the auth lib?
Solution
You very likely need to refactor the code a bit to allow the App
component to use the useAuthentication
hook so it can access the login
function so it can be passed to the loginAction
handler.
Example:
'./routes/login'
import { redirect } from "react-router-dom";
const action = ({ login }) => async ({ request, params }) => {
const formData = await request.formData();
const updates = Object.fromEntries(formData);
// call login, and redirect upon success
await login(...);
...
redirect('/dashboard');
};
App
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Login, { action as loginAction } from './routes/login';
function App() {
const { login } = useAuthentication();
const router = createBrowserRouter([
....,
{
path: '/login',
element: <Login />,
action: loginAction({ login }),
},
....
]);
return (
<RouterProvider router={router} />
);
}
index.js
import { AuthProvider } from 'auth';
...
<AuthProvider>
</App />
</AuthProvider>
AuthenticationContext
const AuthContext = React.createContext(null);
const AuthProvider = ({ children }) => {
const [jwt, setJwt] = useState(null);
const login = async (email, password) => {
const jwt = await loginApi(email, password);
setJwt(jwt);
// return success/fail for login action handler
}
const contextValue = {
jwt,
login: handleLogin,
}
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
}
Answered By - Drew Reese
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.