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.