React-Admin:如何在authProvider中派发store action?
Great question! Since React-Admin's authProvider isn't a React component, you can't use connect or React-Redux hooks like useDispatch directly—but there are straightforward ways to access the Redux store's dispatch method to fire your custom actions, even during the AUTH_LOGIN flow. Let's walk through the two most common approaches:
1. Pass dispatch to your authProvider during initialization
This is the cleanest approach, especially if you want to keep your authProvider decoupled from the store singleton.
First, set up your Redux store with your custom reducer:
// store.js import { createStore, combineReducers } from 'redux'; import { adminReducer } from 'react-admin'; import accountReducer from './accountReducer'; // Your custom reducer const rootReducer = combineReducers({ admin: adminReducer, account: accountReducer, // Add your reducer to the root }); export const store = createStore(rootReducer);
Next, create an authProvider that accepts dispatch as a parameter, then use it in your login flow:
// authProvider.js export const createAuthProvider = (dispatch) => ({ login: async ({ username, password }) => { // Step 1: Call your login endpoint const loginRes = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ username, password }), headers: { 'Content-Type': 'application/json' }, }); if (!loginRes.ok) throw new Error('Login failed'); const { token } = await loginRes.json(); localStorage.setItem('authToken', token); // Step 2: Fetch account data from your separate endpoint const accountRes = await fetch('/api/account', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, }); if (!accountRes.ok) throw new Error('Failed to fetch account data'); const accountData = await accountRes.json(); // Step 3: Dispatch your custom action dispatch({ type: 'ACCOUNT_SET_DATA', // Your action type payload: accountData, }); return Promise.resolve(); }, // Handle logout by clearing account data too logout: () => { localStorage.removeItem('authToken'); dispatch({ type: 'ACCOUNT_CLEAR_DATA' }); return Promise.resolve(); }, // Implement other required authProvider methods checkAuth: () => localStorage.getItem('authToken') ? Promise.resolve() : Promise.reject(), checkError: () => Promise.resolve(), getPermissions: () => Promise.resolve(), });
Finally, pass the store's dispatch to your authProvider when initializing the Admin component:
// App.js import { Admin, Resource } from 'react-admin'; import { store } from './store'; import { createAuthProvider } from './authProvider'; // Import your resources here const App = () => ( <Admin store={store} authProvider={createAuthProvider(store.dispatch)} > {/* Add your resources */} </Admin> ); export default App;
2. Import the store singleton directly
If you don't mind a tighter coupling between your authProvider and store, you can import the store directly and call store.dispatch():
// authProvider.js import { store } from './store'; export const authProvider = { login: async ({ username, password }) => { // ... same login and account fetch logic as above ... // Dispatch action directly from the store store.dispatch({ type: 'ACCOUNT_SET_DATA', payload: accountData, }); return Promise.resolve(); }, // ... other authProvider methods ... };
This works because your store is a singleton, so you can access it anywhere in your codebase. Just note this approach might cause issues with server-side rendering (if you're using it), but it's perfectly fine for client-only apps.
Key Notes
- Make sure your custom reducer is properly combined into the root reducer—otherwise, your dispatched actions won't update the state.
- Always handle errors in your async flows: if fetching account data fails, you might want to clear the auth token and throw an error to notify the user.
- If you're using TypeScript, define action types and payload interfaces to keep your code type-safe.
内容的提问来源于stack exchange,提问作者user2078023

