You’ve been using useSelector hook wrong

Okay, you’ve been probably using useSelector hook wrong. TLDR; don’t use destructuring with useSelector hook. Interested to know more? Let’s say that in your store you have a user object that looks like this.

export const state = {
  user: {
    id: 1,
    email: 'foo@bar.baz',
    name: 'Foo',
    plan: 'standard',
    photo: 'http://foobar.baz/user-1.jpg',
    // other properties
  },
}

Now, let’s say you have a React component that renders a user’s name and photo. We will use useSelector hook to get the name and photo.

import React from 'react'
import { useSelector } from 'react-redux'

export const UserInfo = () => {
  const { name, photo } = useSelector(state => state.user)

  return (
    <div>
      <img src={photo} />
      <span>{name}</span>
    </div>
  );
}

The problem with useSelector hook

At first, this looks fine and everything works as expected but there’s one big downside. Destructuring and useSelector hook don’t play nice together. This component will re-render every time name and photo properties are changed. But it will also re-render if any other user object property gets changed. This could be a huge performance hit depending on how complex your app is.

Don’t use destructuring and useSelector hook at the same time

The solution is to refactor component to avoid destructuring like this.

import React from 'react'
import { useSelector } from 'react-redux'

export const UserInfo = () => {
  const name = useSelector(state => state.user.name)
  const photo = useSelector(state => state.user.photo)
  
  return (
    <div>
      <img src={photo} />
      <span>{name}</span>
    </div>
  );
}

One could argue that this is worse and involves more code repetition, especially if you need to pull a lot of data. In that case, you’re probably better off using connect and mapStateToProps.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.