Dear fellow React developers.
Can someone explain or show what should be the best approach whilst using MSAL in conjunction with React Router in its current latest version 6.4.1 when you want to use the new loader feature?
Based on the new loader feature that you can read about here:
"Each route can define a "loader" function to provide data to the route element before it renders."
https://reactrouter.com/en/main/route/loader
The question arises in me because I'm lacking in imagination here... the only way I can recall to have access to the msal instance is by using the hooks the MSAL library provides when on a component inside the MsalProvider. However, the new loader feature relies on a plain function where I can't use hooks or access any application state.
Any help will be very welcome. Thanks in advance.
To give a little context below I share some of my application code.
in my UserEditScreen.tsx:
// UserEditScreen component definition
....
// loader definition outside UserEditScreen component
export const loader = ({ request, params }: LoaderFunctionArgs) => {
...
// some more code to clarify my need on the msal instance inside the loader
const request = {
account: msalInstance.getActiveAccount(),
scopes: scopes
}
const accessToken = await msalInstance.acquireTokenSilent(request)
.then(response => response.accessToken)
.catch(error => {
console.error(error);
})
// then with the accessToken I can just use made
// a javascript fetch request to my secured endpoint in azure
...
}
in my app.tsx
type AppProps = {
msalInstance: PublicClientApplication;
}
const router = createBrowserRouter(createRoutesFromElements(
<Route path="/" errorElement={<GlobalError />}>
...
<Route path="/administration" element={
<AuthenticatedScreen>
<AdministrationScreen />
</AuthenticatedScreen>
}>
<Route path='users' element={<UsersScreen />} />
<Route path='users/new' element={<UserNewScreen />} />
<Route path='users/:id/edit'
element={<UserEditScreen />}
loader={userLoader}
errorElement={<UserEditError />}
/>
</Route>
...
</Route>
));
function App({ msalInstance }: AppProps) {
return (
<MsalProvider instance={msalInstance}>
<RouterProvider router={router} />
</MsalProvider>
);
}
export default App;
in my index.tsx
const msalInstance = new PublicClientApplication(msalConfig);
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
msalInstance.setActiveAccount(accounts[0]);
}
msalInstance.addEventCallback((event: EventMessage) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
const payload = event.payload as AuthenticationResult;
const account = payload.account;
msalInstance.setActiveAccount(account);
}
});
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App msalInstance={msalInstance} />
</React.StrictMode>
);
msalInstance
instance andMsalProvider
component relates to any of the routing code. Is there some code or functionality that was inadvertently omitted that shows any coupling? Can you clarify what you are trying to do here and what the issue is? See minimal reproducible example. – SeesawmsalInstance
in the loader function? It looks like you've defined someloader
function already with it enclosed. – Seesaw