Unstable selectors causes infinite loop in React
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Needs clarification
- Activity status
- Stale
- Tech stack
- react, typescript
- Domain
- frontend
Research direction
Start by reading src/components/connect.tsx and src/connect/selectorFactory.ts, then compare their memoization with the stability check in src/hooks/useSelector.ts. Reproduce the unstable-selector behavior if possible and review the linked React useSyncExternalStore check. Done means the reported infinite-update case is addressed and connect has a defined stability behavior.
Written by the indexing model from the issue text.
Description
What version of React, ReactDOM/React Native, Redux, and React Redux are you using?
- React: 18.2.0
- ReactDOM/React Native: 18.2.0
- Redux: 3.7.2 (I know)
- React Redux: 8.1.3
What is the current behavior?
React-redux provides an extra option to check selector stability, but that check:
- has to be explicitly enabled
- is not covering
connectfunction - is a subject of a design flaw
The problem is that selector stability must be enforced
What is the expected behavior?
Redux hard breaks for unstable selectors.
Let me explain problem in details.
Recently we started migration from React-redux v5 to v8. The process was almost frictionless, but later we started experiencing product outages (full or partial page crashes) due to endless updates somewhere deep in React.
One of the problems here - while debugging in production was always letting us find and eliminate the problem, we were never able to reproduce it in unit (jest) or integration(Cypress) tests.
Long story short - every time the problem leading to a crash was unstable mapStateToProps that was one way or another creating endless update loop in React.
Lets check the docs:
a selector that returns a different result reference when called again with the same inputs will cause unnecessary rerenders.... causes the component to rerender after every action is dispatched
– https://react-redux.js.org/api/hooks#selector-result-stability
This description is matching react-redux v5 behavior and is different in v8 due to useSyncExternalStore used underneath. The expected behavior comes from this underlaying hook:
React will re-render the component if getSnapshot return value is different from the last time. This is why, if you always return a different value, you will enter an infinite loop and get this error.
– https://react.dev/reference/react/useSyncExternalStore#im-getting-an-error-the-result-of-getsnapshot-should-be-cached
➡️ In our experience we are getting into infinite loop, but are NOT getting this error.
In fact - React has a "double render"-based check for getSnapshot stability, but react-redux effectively bypasses it via memoization here and here
The outcome of the problem is the following:
connectfunction is missingstabilityCheck. It's not as ease to implement as it was done for useSelector, but this gap should be closed- due to internal implementation of
useSyncExternalStore- stability MUST BE enforced, or React can enter endless loops due to various but yet unknown reasons. useSelectordont have any extra caching layers, and yet the necessity to enforce stability is somewhat unclear. Really wondering why you had to implementstabilityCheckwhile React should detect the absence of memoization and at least throw an error.
Which browser and OS are affected by this issue?
No response
Did this work in previous versions of React Redux?
- Yes
- Dominant language
- TypeScript
- Stars
- 23.4k
- Forks
- 3.3k
- PR merge metrics
- No merged PRs in 30d
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from reduxjs/react-redux
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
reduxjs/react-redux#2416 · 1 comment ·
-
Breaking Change Tooling
Difficulty 5/5 Over a week Newbie friendliness 15/100
reduxjs/react-redux#2415 ·
-
feat!: remove `alternate-renderers` entry point for v10Possibly taken @aryaemami59 claimed this 135 days ago. OpenBreaking Change
reduxjs/react-redux#2336 · 1 reaction · 1 assignee ·
-
Performance
Difficulty 5/5 Over a week Newbie friendliness 25/100
reduxjs/react-redux#2086 · 16 comments · 2 reactions ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
reduxjs/react-redux#2060 · 2 comments · 1 reaction ·
All issues in reduxjs/react-redux
Similar issues
-
Autocomplete still accepts an option after becoming disabledPossibly taken @Xapollon430 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
tashfeenahmed/freellmapi#1920 ·
Maintainers usually reply within 1 day