如何在基于Axios与Redux-Promise的Redux应用中实现加载条(无需Redux-Thunk)
Absolutely! You don’t have to tie yourself to Redux-Thunk to add loading bars to your Redux app—there’s a much simpler approach using a flat state structure and plain Redux + Axios helper functions that avoids extra middleware complexity. Here’s how to implement it:
Start with a straightforward, flat state object to track loading status for each API endpoint (or a global count for a single loading bar). For per-endpoint tracking:
// redux/initialState.js const initialState = { // Flat object mapping API endpoints to loading status loading: { fetchUser: false, getPosts: false, updateProfile: false }, // Your existing state properties user: null, posts: [], error: null };
You only need two simple actions to toggle loading states. The reducer will update the flat loading object directly:
// redux/actions.js export const startLoading = (endpoint) => ({ type: 'START_LOADING', payload: endpoint }); export const stopLoading = (endpoint) => ({ type: 'STOP_LOADING', payload: endpoint }); // redux/reducer.js const appReducer = (state = initialState, action) => { switch (action.type) { case 'START_LOADING': return { ...state, loading: { ...state.loading, [action.payload]: true } }; case 'STOP_LOADING': return { ...state, loading: { ...state.loading, [action.payload]: false } }; // Handle your existing API success/error actions here case 'FETCH_USER_SUCCESS': return { ...state, user: action.payload }; default: return state; } };
Create a reusable utility function to wrap your Axios requests. This function will dispatch the loading actions before and after the call—no thunk required, just pass dispatch directly from your component:
// api/helpers.js import axios from 'axios'; import { startLoading, stopLoading } from '../redux/actions'; export const apiCallWithLoading = async (dispatch, endpoint, axiosConfig) => { // Trigger loading state dispatch(startLoading(endpoint)); try { const response = await axios(axiosConfig); // Return data to dispatch success action in component return response.data; } catch (error) { // Pass error to component for error handling throw error; } finally { // Ensure loading state is cleared even if request fails dispatch(stopLoading(endpoint)); } };
In your React components, use useDispatch and useSelector to trigger the wrapped API calls and display loading bars:
// components/UserProfile.js import { useDispatch, useSelector } from 'react-redux'; import { apiCallWithLoading } from '../api/helpers'; const UserProfile = () => { const dispatch = useDispatch(); const { loading, user, error } = useSelector(state => state); const fetchUser = async () => { try { const userData = await apiCallWithLoading( dispatch, 'fetchUser', { method: 'GET', url: '/api/users/me' } ); dispatch({ type: 'FETCH_USER_SUCCESS', payload: userData }); } catch (err) { dispatch({ type: 'FETCH_USER_ERROR', payload: err.message }); } }; return ( <div className="profile-container"> {/* Show loading bar for this specific endpoint */} {loading.fetchUser && <div className="loading-bar">Loading user data...</div>} <button onClick={fetchUser} disabled={loading.fetchUser}> Load My Profile </button> {user && <p>Welcome, {user.name}!</p>} {error && <p className="error">{error}</p>} </div> ); };
If you don’t need per-endpoint tracking and just want a single global loading bar, simplify the state to a count instead of a flat object:
// initialState.js const initialState = { loadingCount: 0, // ...other state }; // actions.js export const incrementLoading = () => ({ type: 'INCREMENT_LOADING' }); export const decrementLoading = () => ({ type: 'DECREMENT_LOADING' }); // reducer.js case 'INCREMENT_LOADING': return { ...state, loadingCount: state.loadingCount + 1 }; case 'DECREMENT_LOADING': return { ...state, loadingCount: Math.max(0, state.loadingCount - 1) };
Then update the helper function to increment/decrement the count, and show the loading bar in your root App component whenever loadingCount > 0.
内容的提问来源于stack exchange,提问作者tcoulson

