DEV Community

Cover image for Why I prefer "connect" over "useSelector" using redux?
sarioglu
sarioglu

Posted on

Why I prefer "connect" over "useSelector" using redux?

It's been almost two years since new hooks have arrived into react-redux and I've had enough chance to work on multiple long-running projects that uses both the old and new approaches to connect with Redux. Here is my conclusion for short: connect is better that useSelector. Here is why:

In version 7.1.1, react-redux introduced their hooks API and updated their website with a tip that endorses hooks over the old higher-order component approach:

We recommend using the React-Redux hooks API as the default approach in your React components.
The existing connect API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript.

Toy Story scene with

React hooks made a great success since it's first introduction. It enables us to write tidy and understandable logic blocks. Yet, it doesn't mean that it is a one-size-fits-all solution for every use-case.

Ease of use

I must admit that hooks are easier to grasp and use than connected components. Using connect requires more boilerplate code. It also requires to know concepts like higher-order components, bindActionCreators, etc. to understand how it actually works. On the other hand, knowing how hooks work is enough to understand useSelector and useDispatch correctly. However ease of use is not always the most important think that we consider while choosing one approach over another.

Redux is really powerful when it comes to managing application state. But with great power comes great responsibility. We should give importance to how state is structured and be picky about what should be included into it and what is not. Only the data that is needed to be long-lived and globally available should make into Redux state. From this perspective, ease of use becomes our enemy. As React's useState hook and Redux's useSelector hook offer similar API surfaces, developers tend to put most of the state to their Redux state instead of picking only necessary ones. In the long run, it becomes bloated and structured by what components need rather than data itself.

Consider following example:

const SomeComponent = () => {
  const dispatch = useDispatch();

  return (
    <div>
      {/* Component content goes here */}
      <button onClick={() => dispatch(openConfirmDialog())}>Click Me!</button>
    </div>
  );
};

const ConfirmDialog = () => {
  const isOpen = useSelector(state => state.foo.bar.isDialogOpen);

  return isOpen ? <div>{/* Dialog content goes here */}</div> : null;
};
Enter fullscreen mode Exit fullscreen mode

In most cases, you don't have to put isOpen into your global application state. Avoiding prop drilling is not an excuse.

The problem here is not caused by useSelector itself. However it makes making mistakes easier. On the other hand, if we were using connect instead, we would think twice to put it into global state.

Maintainability

As Software Developers, our primary job is maintenance of existing code, not writing new ones. As once Martin Fowler said,

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."

Before React's introduction of hooks, we used to use presentational components and container components to make sure that business logic is separate from UI code. Hooks have changed that approach. We can now put business logic into custom hooks and use them in multiple components. However, the same is not correct anymore for UI code. Hooks are directly wired into them and it is not possible to use them by connecting to different data sources.

useSelector is a good example of such cases. I've seen this in many codebases: Components and their sub-components were so tightly coupled into Redux state and therefore developers tended to copy their UI code and create another component instead of using the already available ones. However the solution is simple: Use connect to create a container component and let the presentational part independently available for future uses. It provides just a right amount of abstraction without much complexity.

Loose coupling

Another problem caused by using useSelector arises while writing tests for your code. As the hooks are embedded directly into your component it is impossible to test them independent of the application state. Thus, even the simplest components are required to be connected to Redux.

connect prevents this from happening as well. You can always test your presentational component independently. This allows us to write unit tests specific to that component without connecting it to Redux populated with mock data.

Conclusion

These differences might seem trivial at first glance. But it becomes more and more obvious as project grows. Therefore I suggest to use connect instead of useSelector on your projects as well. It will make things harder, yes, but sometimes in software development making something slightly harder is better to prevent immature decisions taken.

Top comments (2)

Collapse
 
pengeszikra profile image
Peter Vivo

Good reason to use redux instead hooks. I use redux and redux-saga more than 2 years, but at last one year I turn to simple hooks and redux-saga, without redux.
My decision lead the easy component testing. That means my components works without connect dependency.
Instead of couple useState, I use useReducer which is same action-reducer pattern in react than redux without any boilerplate. If you combine dispatch with action than you use your action with really simple.
Redux-saga without redux: useSagaReducer

Collapse
 
sarioglu profile image
sarioglu

Good point. Sometimes I just need to use API response locally. But having redux-saga already in place forces me to use redux store instead. Your solution combines best of two worlds.