Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Invoke hook from React Router data router action

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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>
  );
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading