2

I have just found out that I can use data hooks in createSelector functions and it works. An example:

// This is a normal hook
const useUserReducer = () => {
  const userAccessData = useSelector(state => state?.userAccessData)
  return userAccessData
}

// Here I use the hook as first argument!
export const useUserReducerFromCreateSelector = createSelector(useUserReducer, (result) => {
  console.log(result) // userAccessData printed correctly
  return result
})

Then I use it in my component as a normal hook:

const Component = () => {
  const result = useUserReducerFromCreateSelector([])
  console.log(result) // userAccessData printed correctly
  return (
    <>
      {JSON.stringify(result)}
    </>
  )
}

I dont see any documentation about this, so I wonder if its safe to use it. It would help me a lot creating reusable selectors.

(I tested while changing the state at various points in time and I always see the correct state)

Rashomon
  • 5,962
  • 4
  • 29
  • 67
  • By "it works" you mean you tested while changing the state at various points in time and you always see the correct state or do you mean "I don't get an error" ? – GACy20 May 24 '22 at 07:02
  • I tested while changing the state at various points in time and I always see the correct state. I update the question. – Rashomon May 24 '22 at 07:03

2 Answers2

3

It is certainly an abuse if it is working. createSelector is only supposed to be a pure state selector function, so naming the returned selector function like a React hook, i.e. useUserReducerFromCreateSelector, is likely to cause some linter warnings eventually.

The potential issue is that Reselect and createSelector creates memoized selector functions. If the input value to a selector doesn't change, then the selector function returns the previously computed selector value. This means that a selector using a React hook like this potentially conditionally calling a React hook which is a violation of the Rules of Hooks.

Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)

Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions. Instead, you can:

  • ✅ Call Hooks from React function components.
  • ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).

By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

I don't consider it safe to use any React hook in a selector function like this.

Split out the logic of selecting the state from the useUserReducerFromCreateSelector hook to be used in your selector functions.

Example:

const userAccessData = state => state?.userAccessData || {};

const computedUserAccessData = createSelector(
  [userAccessData],
  data => {
    // logic to compute derived state, etc...
    return newUserAccessData;
  },
);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
1

I was really intrigued seeing this particular use in the redux-toolkit github repo issues and nobody complaining about it so I decided to ask the same question in the reselect github page.

Here is the response of Mark Erikson (redux maintainer):

No, this is not safe!

You're technically getting away with it because of how you're using that in a component. But if you were to try to use that selector outside of a component, it would break.

I'd really recommend sticking with keeping these concepts separate. Write and name selectors as selectors. Write and name hooks as hooks. Don't try and mix the two :)

To be clear, the code that you wrote above should run. It's ultimately "just" composition of functions and calling them in a particular order.

But given how hooks work, and how selectors work, it's best to keep those concepts separate when writing the code to avoid confusion.

Rashomon
  • 5,962
  • 4
  • 29
  • 67