Search code examples
reactjsreduxreact-reduxrenderreact-redux-connect

React-redux rerenders when creating arrays


I have a connected component where I would like to retrieve an array of objects. In my store, there is an array of ids, and an object where I keep the items like this:

const state = {
  items: [0, 1, 2],
  itemsById: {
    0: {},
    1: {},
    2: {},
  },
}

So using the connect function of react-redux, I do this in my component to inject the correct array:

const mapStateToProps = (state) => ({
  items: state.items.map((itemId) => state.itemsById[itemId]),
})

My app triggers updates very often (I dispatch actions in requestAnimationFrame), but the items array do not change during the process. By analyzing the app with the React Perf addon, it seems that my connected component makes unnecessary rendering, and I don't understand why because the items in the state don't change.

I already tried to make a memoized selector using reselect but it seems it does not change anything.

Update (solution)

It works when you use a selector created with reselect. My problem was in the selector itself: my items array is located in a parent object which updates very frequently, and I was selecting this object instead of selecting the items array directly.

Don't do this:

const parentSelector = (state) => state.parent
const itemsByIdSelector = (state) => state.itemsById
const selector = createSelector(
  parentSelector,
  itemsByIdSelector,
  (parent, itemsById) => parent.items.map(...)
)

Do this:

const itemsSelector = (state) => state.items
const itemsByIdSelector = (state) => state.itemsById
const selector = createSelector(
  itemsSelector,
  itemsByIdSelector,
  (items, itemsById) => items.map(...)
)

Solution

  • You are creating a new array every time connect is called by doing this:

    const mapStateToProps = (state) => ({
      items: state.items.map((itemId) => state.itemsById[itemId]),
    })
    

    To prevent that use a memoized selector, which will return the same array every time, unless something actually changed. A selector is a method that computes the derived data from the state. Reselect is a memoized selector library for redux.