3

I have a React application where I'm using Redux and Redux-Saga and Reselect as selector library (with Immer to handle the immutability of the Redux state). I'm writing this question because I'd like to understand if my approach to handle the selector with argument is fully correct.

I personally prefer to avoid the yield select into the sagas because I'd like to keep the middleware not depend on the Store's state, but I have legacy code that has sagas with yield select in it.

Here below the code that I have to write to implement my selector and invoke him into the component and into the saga. I got the selector implementation from the Reselec doc.

// selectors.js: Selector implementation
const makeGetPerson = createSelector(
  getPersonsList,
  (state, id) => id,
  (persons, id) => persons.find((person) => person.id === id)
);

// components.js: if I have to use the selector into a component
const person = useSelector((state) => makeGetPerson(state, id))


// saga.js: if I have to use the selector into a saga
const persons = yield select(getPersonsList)
const person = persons.find((person) => person.id === id)

At the moment from the saga I have to use the selector that return the list of element and manually filter them. Is there a way to do something like yield select((state) => getPersonsList(state, id)) into the a saga ? Is there any other approach that I'm missing ?

lucataglia
  • 728
  • 5
  • 18
  • Nitpick: don't call the selector `makeGetPerson`. The "make" prefix is typically used for "selector factories" that return a new selector instance every time they're called. This isn't a "factory" - it's the selector function itself. Call it just `getPerson` or `selectPerson`. See the Redux docs [Deriving Data with Selectors](https://redux.js.org/usage/deriving-data-selectors) page for more details. – markerikson Nov 09 '21 at 17:01

2 Answers2

2

The select effect creator accepts additional arguments after the selector function which are then send to the selector itself, so you can do this:

const person = yield select(makeGetPerson, id);

Docs: https://redux-saga.js.org/docs/api/#selectselector-args

Martin Kadlec
  • 4,702
  • 2
  • 20
  • 33
0

You could also make the createSelectPersonById a curry:

const createSelectPersonById = id => createSelector(
  getPersonsList,
  (persons) => persons.find((person) => person.id === id)
);
// components
const person = useSelector(createSelectPersonById(id))
//saga
const person = yield select(createSelectPersonById(id));

In the select person by id you are just returning state and not a new object but if you did return a new object every time you could use useMemo in your component so you don't get needless re renders when unrelated actions are dispatched:

//selector returning new object every time it is executed
const createSelectPersonById = (id) =>
  createSelector(getPersonsList, (persons) => {
    const person = persons.find(
      (person) => person.id === id
    );
    //returning a new object reference every time
    return {
      ...person,
      fullName:formatFullName(person)
    }
  });
//in components
const selectPersonById = React.useMemo(
  ()=>createSelectPersonById(id),
  [id]//selector only re created when id changes
)
const person = useSelector(selectPersonById);
//in saga nothing changes, no need to memoize:
const person = yield select(createSelectPersonById(id));

More info on how and why I make parameterized selectors a curry in React can be found here

HMR
  • 37,593
  • 24
  • 91
  • 160
  • The curry approach + useMemo could work, I haven't thought about that. I'm having the feeling that use the useMemo in the component having Reselect for the selector is a little redundant. Using the useSelector with the callback syntax for components and the yield select with varargs for the sagas can help building a linear architecture. What do you think ? – lucataglia Nov 10 '21 at 11:45
  • @lucataglia `I'm having the feeling that use the useMemo in the component having Reselect for the selector is a little redundant.` Not if your selector returns a new reference (in your code example it doesn't) Reselect memoization doesn't work at all if you have a list rendering and every component calls the selector with different a different id unless the component has its own selector. – HMR Nov 10 '21 at 13:00
  • @lucataglia I have updated the repo trying to explain this a little bit more clearly: [paremeterized and memoized selectors](https://github.com/amsterdamharu/selectors#parameterized-and-memoized) – HMR Nov 10 '21 at 13:11