如何在Redux的Action/Service中访问Store?重复代码优化咨询
Hey there! No worries at all—switching from Ember to React+Redux takes time, and it’s totally normal to hit these little roadblocks as you learn the patterns. Let’s break down how you can eliminate that repeated code and handle the "check cache first, fetch if missing" logic properly.
1. Use Redux Thunk to Add Logic to Action Creators
The most common way to handle this is with Redux Thunk (a middleware that lets your action creators return functions instead of plain objects). These functions get access to dispatch and getState, so you can check the current store state before deciding to fetch data.
First, make sure you have Redux Thunk set up in your store (most Redux apps use it by default). Then, create a reusable action creator like this:
// userActions.js export const getProfile = (userId) => async (dispatch) => { // Your existing API fetch logic here const response = await fetch(`/api/profiles/${userId}`); const profileData = await response.json(); dispatch({ type: 'PROFILE_FETCH_SUCCESS', payload: profileData }); }; // The new reusable action that checks cache first export const getProfileIfNeeded = (userId) => (dispatch, getState) => { // Get the current profiles from the store const { profiles } = getState().profileaccounts; // Check if the profile already exists const existingProfile = profiles.find(prof => prof.profile.id === userId); if (!existingProfile) { // Only fetch if it's not in the cache return dispatch(getProfile(userId)); } // If it exists, return the cached data (as a Promise for consistency) return Promise.resolve(existingProfile); };
Then, in all your components, replace that repeated componentDidMount code with:
componentDidMount() { const { user, dispatch } = this.props; dispatch(getProfileIfNeeded(user.id)); }
2. Use a Custom Hook (For Functional Components)
If you’re moving towards functional components (the modern React approach), wrap this logic in a custom hook for even easier reuse:
// hooks/useProfile.js import { useSelector, useDispatch } from 'react-redux'; import { getProfileIfNeeded } from '../actions/userActions'; import { useEffect } from 'react'; export const useProfile = (userId) => { const dispatch = useDispatch(); const profiles = useSelector(state => state.profileaccounts.profiles); const currentProfile = profiles.find(prof => prof.profile.id === userId); useEffect(() => { if (userId && !currentProfile) { dispatch(getProfileIfNeeded(userId)); } }, [userId, currentProfile, dispatch]); return currentProfile; };
Now any functional component can use this hook in one line:
const profile = useProfile(user.id);
3. For Class Components: Use a Higher-Order Component (HOC)
If you’re sticking with class components, create an HOC that injects the profile and handles the fetch logic:
// hoc/withProfile.js import React from 'react'; import { connect } from 'react-redux'; import { getProfileIfNeeded } from '../actions/userActions'; const withProfile = (WrappedComponent) => { class WithProfile extends React.Component { componentDidMount() { const { user, getProfileIfNeeded } = this.props; if (user?.id) { getProfileIfNeeded(user.id); } } componentDidUpdate(prevProps) { // Re-fetch if the user changes if (this.props.user?.id !== prevProps.user?.id) { this.props.getProfileIfNeeded(this.props.user.id); } } render() { const { profiles, user, ...rest } = this.props; const currentProfile = profiles.find(prof => prof.profile.id === user?.id); return <WrappedComponent profile={currentProfile} {...rest} />; } } const mapStateToProps = state => ({ profiles: state.profileaccounts.profiles }); const mapDispatchToProps = { getProfileIfNeeded }; return connect(mapStateToProps, mapDispatchToProps)(WithProfile); }; export default withProfile;
Then wrap your components with it:
class MyComponent extends React.Component { render() { const { profile } = this.props; // Use profile here } } export default withProfile(MyComponent);
Key Takeaway
You don’t need to access the store directly from services—instead, use middleware like Redux Thunk to let your action creators check the store state before triggering API calls. This keeps your logic centralized and eliminates duplicate code across components.
内容的提问来源于stack exchange,提问作者Richard Mathis

