question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

useSelector's provided selector function is called twice on mount

See original GitHub issue

What version of React, ReactDOM/React Native, Redux, and React Redux are you using?

  • React: 17.0.2
  • ReactDOM/React Native: 17.0.2
  • Redux: 4.1.1
  • React Redux: 7.2.

What is the current behavior?

While refactoring some connect()-based components to be functional hook-based components using useSelector(), I noticed some degraded performance.

It looks like on initial mount, selectors passed to useSelector() are called twice when they only need to be called once.

A test case demonstrating the calls and call counts can be shown here: https://codesandbox.io/s/use-selector-twice-lgovv?file=/src/index.js

Upon loading, the console log shows:

1. useSelector: render 
1. useSelector: useSelector 
2. useSelector+useCallback: render 
2. useSelector+useCallback: useSelector 
3. mapStateToProps: mapStateToProps 
3. mapStateToProps: render 
1. useSelector: useSelector 
2. useSelector+useCallback: useSelector

The last two useSelector calls are surprising, as they occur after their corresponding components have rendered, but themselves do not trigger a call to the functional component.


As an aside, I don’t believe this is an issue, but did notice some undocumented/surprising behavior:

If you pass an inline function to useSelector, like:

function MyComponent({ myValue }) {
    const myResult = useSelector((state) => getMyResult(state, myValue));
    // ...
}

getMyResult will be called twice for each redux subscriber notification, this is due to useSelector seeing that the selector function passed has differed, and as a result will call the selector function. To avoid this, the selector passed to useSelector can be memoized:

function MyComponent({ myValue }) {
    const getMyResultMemo = useCallback((state) => getMyResult(state, myValue), [myValue]);
    const myResult = useSelector(getMyResultMemo);
    // ...
}

You can see this difference when interacting with the +1 buttons in the linked demo.

What is the expected behavior?

I would expect mounting a component to only call each selector passed to useSelector once, similar to how on mount connect() calls the mapStateToProps function only once.

In the linked demo, I would have expected the console output on load to read:

1. useSelector: render 
1. useSelector: useSelector 
2. useSelector+useCallback: render 
2. useSelector+useCallback: useSelector 
3. mapStateToProps: mapStateToProps 
3. mapStateToProps: render 

Which browser and OS are affected by this issue?

all

Did this work in previous versions of React Redux?

  • Yes

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
markeriksoncommented, Aug 24, 2021

Yep!

I can always put out new 7.x patch releases - we’re still maintaining the 7.x line, we’re just starting to work towards an 8.0 on master. (For that matter, I figured out a way to shave some bytes from our Subscription implementation during the TS conversion process that I need to apply to 7.x as well.)

0reactions
sufian-slackcommented, Aug 24, 2021

Thanks, just added two pull requests, one for the 7.x line and the other for master.

And thanks for the feedback on not needing to keep track of the first render, the change is much simpler!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why does the selector inside useSelector run twice?
Because it runs both in the render phase, and after an action is dispatched. So, the first log happens when <App> is rendered, ......
Read more >
Hooks - React Redux
You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to ...
Read more >
How useSelector can trigger an update only when we want it to
When the subscription fires, it calls a function within each instance of useSelector that checks whether its internal reference to selected ...
Read more >
UseSelector and UseDispatch: A Guide to React-Redux Hooks
This guide will cover how to implement the React-Redux hooks useSelector and useDispatch in your application.
Read more >
React Redux connect(): When and how to use it
Editor's note: This article was last updated 16 June 2022 to include the use of the useDispatch and useSelector Hooks in React functional...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found