Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by MartianStargazer091

How can I minimize Redux re-renders when filtering active entities?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I want to access only the active entities from my Redux store (i.e. store.entities), but I’m noticing unnecessary re-renders because filtering creates a new array reference each time. For example, this approach is problematic:

JAVASCRIPT
const activeEntities = useSelector( state => state.entities.filter(entity => entity.active) );

The alternative I tried was moving the filter outside the selector:

JAVASCRIPT
const activeEntities = useSelector(state => state.entities) .filter(entity => entity.active);

This seems to ensure the component only re-renders when state.entities changes. My question is: can I improve this behavior further using memoization? For instance, I considered using Reselect like this:

JAVASCRIPT
const selectActiveEntities = createSelector( (state) => state.entities.items, (items) => items.filter(entity => entity.active) );

In my mind, this is functionally equivalent to the previous example. Ideally, I want the component to re-render only when the filtered active entities change in value. How can I achieve this?

2

Comments 0

Answers 3

Views 61

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is an explanation of how to use memoization effectively (using reselect) and why it can help.


What’s Happening Right Now

  • Direct filtering in the selector:

    JS
    const activeEntities = useSelector( state => state.entities.filter(entity => entity.active) );

    Every time the selector runs, it creates a new array. This means even if the underlying active entities haven’t changed, the component may re-render because the reference is different.

  • Filtering outside the selector:

    JS
    const activeEntities = useSelector(state => state.entities) .filter(entity => entity.active);

    Now the component depends solely on state.entities for triggering a re-render. If you update something irrelevant (or even if the list is re-created with the same objects), you could trigger unnecessary renders.


Using Reselect for Memoization

Memoization via reselect will help by:

  • Caching the filtered result so that if the underlying data that the filter depends on (i.e. the active entities) hasn't changed, the same reference is returned.
  • Avoiding unnecessary re-renders by ensuring that the component only updates when the content of active entities actually changes.

A typical setup would be:

JS
import { createSelector } from 'reselect'; const selectEntities = state => state.entities; export const selectActiveEntities = createSelector( [selectEntities], entities => entities.filter(entity => entity.active) );

Then in your component:

JS
import { useSelector } from 'react-redux'; import { selectActiveEntities } from './selectors'; const MyComponent = () => { const activeEntities = useSelector(selectActiveEntities); // render your component... return ( <div> {activeEntities.map(entity => ( <div key={entity.id}>{entity.name}</div> ))} </div> ); };

Key Points

  • Memoization Benefit:
    When state.entities changes, even if a new array is passed in, the memoized selector will recompute the filtered result only if the relevant parts (the “active” entities) have really changed. Otherwise, it will return the previous cached result.

  • Immutable Data Practices:
    For reselect to work optimally, it's best if your updates follow immutable practices. That way, if there’s no meaningful change, the reference equality can be preserved for unchanged items.

  • When is it Functionally Equivalent?
    Your second example (filtering outside the selector) is functionally equivalent in terms of getting the filtered list. However, it won’t memoize the filtered array. Using createSelector ensures that if no active entity has changed, the same array reference is returned, which can help prevent unnecessary renders.


Conclusion

Using memoized selectors (like with reselect's createSelector) is the best practice here. It helps ensure that your component only re-renders when the underlying active entities actually change, rather than every time the global state updates with new references.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by StarAdventurer560 1 month ago

0
JAVASCRIPT
undefined

const selectActiveEntities = createSelector(
(state) => state.entities.items,
(items) => items.filter(entity => entity.active)
);


This *may be* functionally equivalent to your first examples doing the work directly in or around the `useSelector` hook, but I suspect it's already providing the memoized result you want. `const activeEntities = useSelector(selectActiveEntities);` should be fine.

The only "improvement" I'd suggest is splitting up the selector such that you can memoize intermediate results. I believe it's preferable for selectors to work only 1-level deep when accessing into state properties, so each can be memoized for consumption by UI components or when used as input selector functions to other selectors.

Example:

```javascript
const selectEntities = state => state.entities;

const selectEntityItems = createSelector(
  [selectEntities],
  entities => entities.items,
);

const selectActiveEntities = createSelector(
  [selectEntityItems],
  (entityItems) => entityItems.filter(entity => entity.active),
);

JAVASCRIPT
import { useSelector } from 'react-redux'; import { selectActiveEntities } from '../path/to/selectors'; ... const activeEntities = useSelector(selectActiveEntities);

If you've further need to hint at selector value equality, especially when working with arrays, you should use the shallowEqual utility or custom equality function with useSelector.

See Equality Comparisons and Updates for complete details.

Example:

JAVASCRIPT
import { shallowEqual, useSelector } from 'react-redux'; import { selectActiveEntities } from '../path/to/selectors'; ... const activeEntities = useSelector(selectActiveEntities, shallowEqual);

No comments yet.

Answer by StellarOrbiter982 1 month ago

0

You are right about the point that even the third example is equivalent in terms of the rendering it will cause.

How createSelector() works is that the outputSelector will not run if the inputSelector output does not change, but if your state array reference changes, it will run. And in that case, since the output selector is using a .filter(), a new array will be created even if the active values are still the same.

Since you want to prevent rerender based on the active values you can use a customEquality function, which is passed in as the second argument of useSelector.

For example if you have the ids of each of your entities, ensure that you only rerender when they change:

JAVASCRIPT
const idCheck = (prevArr,newArr) => { //create array of ids const prevArrIds = prevArr.map(({id}) => id); const newArrIds = newArr.map(({id}) => id); //sort array prevArrIds.sort((a,b) => a-b); newArrIds.sort((a,b) => a-b); //join the array into a string and compare return prevArrIds.join('') !== newArrIds.join('') }; ... const activeEntities = useSelector(state => state.entities.filter(entity => entity.active), idCheck);

Of course the above idCheck will now always run when the store value changes.

The optional comparison function also enables using something like Lodash's _.isEqual() or Immutable.js's comparison capabilities. There are is shallowEqual from react-redux.

A minor optimization you can do is use the selector created from createSelector and then still use this custom equality method.

No comments yet.

Discussion

No comments yet.