Best practice in manipulating redux state locally in React component

I was wondering what was the best practice in manipulating data that is fed from my redux store to my component. In case I need to merge data from two different reducers or need to further sanitize some data.

Imagine I have a list of names that came from my redux state like this :

names =  [{ id: 1, name: 'name1' },
           { id: 2, name: 'name2' },
           { id: 3, name: 'name3' },
           { id: 4, name: 'name4' },
           { id: 5, name: 'name5' },]

And a list of attributes that also came from another piece of my state.

attributes =  [{ id: 1, atrib: 'atrib1' },
               { id: 2, atrib: 'atrib2' },
               { id: 3, atrib: 'atrib3' },
               { id: 4, atrib: 'atrib4' },
               { id: 5, atrib: 'atrib5' },]

In the case I need to merge these two and get as a result:

namesAndAttributes =  [{ id: 1, name: 'name1', atrib: 'atrib1' },
                       { id: 2, name: 'name2', atrib: 'atrib2' },
                       { id: 3, name: 'name3', atrib: 'atrib3' },
                       { id: 4, name: 'name4', atrib: 'atrib4' },
                       { id: 5, name : 'name5', atrib: 'atrib5' },]

Imagine it is a long list of data, so I would not like to hard code this merge operation into a variable in the root of my component with a .map(), so it would not recalculate on every render.

Should I create another local state and use an useEffect hook with [names, attributes] as dependencies? Or should I create another field in my reducer’s state and manipulate this data inside my reducer?

If this data that was used in many places of my application, I’d be happy to add another field and action type to my reducer, but if this operation was necessary in only one component of my application?

Is there a right or wrong here? Thanks!

Answer

Well you can use reselect library that goes very well with Redux. https://github.com/reduxjs/reselect

So the idea is to create selector that will depend on states that you want, in your case names and attributes. And only when they change you will get new data and that selector will execute only then.

In your example you would have something like this:

Names in store

names = [
  { id: 1, name: 'name1' },
  { id: 2, name: 'name2' },
  { id: 3, name: 'name3' },
  { id: 4, name: 'name4' },
  { id: 5, name: 'name5' },
];

Attributes in store store

attributes = [
  { id: 1, atrib: 'atrib1' },
  { id: 2, atrib: 'atrib2' },
  { id: 3, atrib: 'atrib3' },
  { id: 4, atrib: 'atrib4' },
  { id: 5, atrib: 'atrib5' },
];

Selector

import { createSelector } from 'reselect';

const getNamesFromReducer1 = (state) => state.reducer1.names;
const getAttributesFromReducer2 = (state) => state.reducer2.attributes;

export const getNamesWithAttributes = createSelector(
  [getNamesFromReducer1, getAttributesFromReducer2],
  (names, attributes) => {
    return names.map((name) => {
      return {
        ...name,
        ...attributes.find((attribute) => attribute.id === name.id),
      };
    });
  }
);

And implementation of that selector

const mapStateToProps = (state) => {
  return {
    todos: getNamesWithAttributes(state)
  }
}