能否通过传入ID数组使用useSelector从Redux Store中选择多个实体?
Great question! The issue you're hitting is a classic React Hook rules violation—you can't call hooks like useSelector inside loops, conditions, or nested functions. Let's fix this by fetching all the entities you need in a single useSelector call instead.
Here are a couple of robust approaches tailored to your Redux Toolkit setup:
1. Basic Custom Selector (Simple Implementation)
You can create a straightforward selector that takes the Redux state and your array of company IDs, then maps over the IDs to pull the corresponding entities from the companies slice.
First, define the selector function:
import { RootState } from '../path/to/your/store'; // Import your root state type const selectCompaniesByIds = (state: RootState, companyIds: number[]) => { // Map over IDs, fetch each entity, and filter out any undefined values return companyIds.map(id => state.companies.entities[id]).filter(Boolean); };
Then use it in your component at the top level:
const companyIds = useSelector(state => state.companyPages.entities[pageNumber]?.companyIds || []); const companies = useSelector(state => selectCompaniesByIds(state, companyIds));
Note the optional chaining (?.) and fallback to an empty array for companyIds—this handles cases where pageNumber doesn't exist in companyPages.entities.
2. Memoized Selector (Performance Optimized)
If your component re-renders frequently or you're working with large ID arrays, use Redux Toolkit's built-in integration with Reselect to create a memoized selector. This ensures the selector only recalculates when either the companies.entities or companyIds array changes, avoiding unnecessary re-renders.
First, create the memoized selector:
import { createSelector } from '@reduxjs/toolkit'; import { RootState } from '../path/to/your/store'; // Create a memoized selector that depends on companies entities and the ID array const selectCompaniesByIds = createSelector( // Input selectors: get the raw entities and the ID array [ (state: RootState) => state.companies.entities, (_state: RootState, companyIds: number[]) => companyIds ], // Result function: compute the final array of companies (entities, companyIds) => { return companyIds.map(id => entities[id]).filter(Boolean); } );
Then use it in your component just like the basic version:
const companyIds = useSelector(state => state.companyPages.entities[pageNumber]?.companyIds || []); const companies = useSelector(state => selectCompaniesByIds(state, companyIds));
Bonus: Leverage Entity Adapter Selectors
Since you're using createEntityAdapter for your slices, you can also use the built-in selectById selector alongside createSelector for consistency:
import { createSelector } from '@reduxjs/toolkit'; import { RootState } from '../path/to/your/store'; import { companyAdapter } from '../path/to/companySlice'; // Get the built-in selectById from your company adapter const { selectById } = companyAdapter.getSelectors((state: RootState) => state.companies); const selectCompaniesByIds = createSelector( [ (state: RootState) => state, (_state: RootState, companyIds: number[]) => companyIds ], (state, companyIds) => { return companyIds.map(id => selectById(state, id)).filter(Boolean); } );
This keeps your code aligned with Redux Toolkit's entity patterns.
Key Takeaways
- Never call hooks like
useSelectorinside loops or nested functions—stick to the top level of your component. - Fetch all required entities in a single
useSelectorcall using a custom selector. - Use memoized selectors with
createSelectorto optimize performance for frequent re-renders or large datasets.
内容的提问来源于stack exchange,提问作者Dima Kambalin

