dai-shi / Will This React Global State Work In Concurrent Mode
Programming Languages
Will this React global state work in Concurrent Mode?
Checking tearing in React concurrent mode
Introduction
In react-redux, there's a theoretical issue called "tearing" that might occur in React concurrent mode.
Let's try to check it!
What is tearing?
- Stack Overflow
- Talk by Mark Erikson
- Talk by Flarnie Marchan
- Some other resources
How does it work?
A small app is implemented with each library. The state has one count. The app shows the count in fifty components.
There's a button outside of React and if it's clicked it will trigger state mutation. This is to emulate mutating an external state outside of React, for example updating state by Redux middleware.
The render has intentionaly expensive computation. If the mutation happens during rendering with in a tree, there could be an inconsistency in the state. If it finds the inconsistency, the test will fail.
How to run
git clone https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-mode.git
cd will-this-react-global-state-work-in-concurrent-mode
yarn install
yarn run build-all
PORT=8080 yarn run http-server &
PORT=8080 yarn run jest
You can also test it by opening http://localhost:8080/react-redux
in your browser, and click the button very quickly. (check the console log)
Screencast
Check scenario
- Normal tearing in Concurrent Mode
- check 1: updated properly
- check 2: no tearing during update
- check 3: ability to interrupt render
- check 4: proper update after interrupt
- Tearing and state branching with useTransition
- check 5: updated properly with transition
- check 6: no tearing with transition
- check 7: proper branching with transition
- Tearing with intentional update in render
- check 8: updated properly with auto increment
- check 9: no tearing with auto increment (currently, it's not really what is intended to test)
Results
Raw Output
react-redux
check with events from outside
✓ check 1: updated properly (2715 ms)
✕ check 2: no tearing during update (3 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1550 ms)
check with useTransition
✓ check 5: updated properly with transition (2342 ms)
✕ check 6: no tearing with transition (92 ms)
✕ check 7: proper branching with transition (5995 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3287 ms)
✕ check 9: no tearing with auto increment (3 ms)
redux-use-mutable-source
check with events from outside
✓ check 1: updated properly (3260 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1221 ms)
check with useTransition
✓ check 5: updated properly with transition (2412 ms)
✓ check 6: no tearing with transition (127 ms)
✕ check 7: proper branching with transition (6223 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3242 ms)
✕ check 9: no tearing with auto increment (4 ms)
reactive-react-redux
check with events from outside
✓ check 1: updated properly (3604 ms)
✓ check 2: no tearing during update (2 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1233 ms)
check with useTransition
✓ check 5: updated properly with transition (2408 ms)
✓ check 6: no tearing with transition (125 ms)
✕ check 7: proper branching with transition (6238 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3255 ms)
✕ check 9: no tearing with auto increment (2 ms)
react-tracked
check with events from outside
✓ check 1: updated properly (4439 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (2232 ms)
check with useTransition
✕ check 5: updated properly with transition (2565 ms)
✓ check 6: no tearing with transition (814 ms)
✓ check 7: proper branching with transition (5304 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (4373 ms)
✓ check 9: no tearing with auto increment (1 ms)
constate
check with events from outside
✓ check 1: updated properly (3434 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1200 ms)
check with useTransition
✓ check 5: updated properly with transition (2366 ms)
✓ check 6: no tearing with transition (69 ms)
✓ check 7: proper branching with transition (3207 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3132 ms)
✓ check 9: no tearing with auto increment (1 ms)
zustand
check with events from outside
✓ check 1: updated properly (3233 ms)
✕ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1145 ms)
check with useTransition
✓ check 5: updated properly with transition (2328 ms)
✕ check 6: no tearing with transition (79 ms)
✕ check 7: proper branching with transition (5966 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3154 ms)
✕ check 9: no tearing with auto increment (1 ms)
react-sweet-state
check with events from outside
✓ check 1: updated properly (3198 ms)
✕ check 2: no tearing during update (1 ms)
✕ check 3: ability to interrupt render (7 ms)
✓ check 4: proper update after interrupt (1176 ms)
check with useTransition
✓ check 5: updated properly with transition (3266 ms)
✓ check 6: no tearing with transition (73 ms)
✕ check 7: proper branching with transition (6095 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3146 ms)
✕ check 9: no tearing with auto increment (21 ms)
storeon
check with events from outside
✓ check 1: updated properly (3244 ms)
✕ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render (1 ms)
✓ check 4: proper update after interrupt (1136 ms)
check with useTransition
✓ check 5: updated properly with transition (2461 ms)
✓ check 6: no tearing with transition (39 ms)
✕ check 7: proper branching with transition (6120 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3187 ms)
✕ check 9: no tearing with auto increment (1 ms)
react-hooks-global-state
check with events from outside
✓ check 1: updated properly (2171 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1210 ms)
check with useTransition
✓ check 5: updated properly with transition (2410 ms)
✓ check 6: no tearing with transition (167 ms)
✕ check 7: proper branching with transition (6300 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3248 ms)
✕ check 9: no tearing with auto increment (1 ms)
use-context-selector
check with events from outside
✓ check 1: updated properly (4337 ms)
✓ check 2: no tearing during update
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (3251 ms)
check with useTransition
✕ check 5: updated properly with transition (2556 ms)
✓ check 6: no tearing with transition (799 ms)
✓ check 7: proper branching with transition (5391 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (4385 ms)
✓ check 9: no tearing with auto increment (1 ms)
mobx-react-lite
check with events from outside
✓ check 1: updated properly (2197 ms)
✕ check 2: no tearing during update (2 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1164 ms)
check with useTransition
✓ check 5: updated properly with transition (2473 ms)
✕ check 6: no tearing with transition (42 ms)
✕ check 7: proper branching with transition (6122 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (2081 ms)
✕ check 9: no tearing with auto increment (1 ms)
use-subscription
check with events from outside
✓ check 1: updated properly (3227 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render (1 ms)
✓ check 4: proper update after interrupt (1275 ms)
check with useTransition
✓ check 5: updated properly with transition (2389 ms)
✓ check 6: no tearing with transition (169 ms)
✕ check 7: proper branching with transition (6291 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3290 ms)
✕ check 9: no tearing with auto increment (1 ms)
mobx-use-sub
check with events from outside
✓ check 1: updated properly (2161 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1182 ms)
check with useTransition
✓ check 5: updated properly with transition (2555 ms)
✓ check 6: no tearing with transition (171 ms)
✕ check 7: proper branching with transition (6240 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (2171 ms)
✕ check 9: no tearing with auto increment (1 ms)
react-state
check with events from outside
✓ check 1: updated properly (3230 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1189 ms)
check with useTransition
✓ check 5: updated properly with transition (2448 ms)
✓ check 6: no tearing with transition (42 ms)
✓ check 7: proper branching with transition (3239 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3118 ms)
✓ check 9: no tearing with auto increment (1 ms)
simplux
check with events from outside
✓ check 1: updated properly (2208 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (2198 ms)
check with useTransition
✓ check 5: updated properly with transition (2439 ms)
✓ check 6: no tearing with transition (41 ms)
✕ check 7: proper branching with transition (6090 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (2238 ms)
✓ check 9: no tearing with auto increment (1 ms)
react-apollo
check with events from outside
✓ check 1: updated properly (3207 ms)
✕ check 2: no tearing during update (2 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (2192 ms)
check with useTransition
✓ check 5: updated properly with transition (2351 ms)
✓ check 6: no tearing with transition (80 ms)
✕ check 7: proper branching with transition (6017 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3034 ms)
✕ check 9: no tearing with auto increment (1 ms)
recoil
check with events from outside
✓ check 1: updated properly (2428 ms)
✓ check 2: no tearing during update
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (2280 ms)
check with useTransition
✕ check 5: updated properly with transition (2556 ms)
✓ check 6: no tearing with transition (815 ms)
✕ check 7: proper branching with transition (6119 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3178 ms)
✓ check 9: no tearing with auto increment (1 ms)
jotai
check with events from outside
✓ check 1: updated properly (3417 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render (1 ms)
✓ check 4: proper update after interrupt (3241 ms)
check with useTransition
✕ check 5: updated properly with transition (2548 ms)
✓ check 6: no tearing with transition (842 ms)
✕ check 7: proper branching with transition (6138 ms)
check with intensive auto increment
✕ check 8: updated properly with auto increment (10211 ms)
✓ check 9: no tearing with auto increment (3 ms)
effector
check with events from outside
✓ check 1: updated properly (3413 ms)
✕ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1165 ms)
check with useTransition
✓ check 5: updated properly with transition (3133 ms)
✕ check 6: no tearing with transition (45 ms)
✕ check 7: proper branching with transition (5993 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3185 ms)
✕ check 9: no tearing with auto increment (21 ms)
react-rxjs
check with events from outside
✓ check 1: updated properly (3308 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1212 ms)
check with useTransition
✓ check 5: updated properly with transition (2282 ms)
✓ check 6: no tearing with transition (77 ms)
✕ check 7: proper branching with transition (5994 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3307 ms)
✓ check 9: no tearing with auto increment (1 ms)
rxdeep
check with events from outside
✓ check 1: updated properly (2552 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render (1 ms)
✓ check 4: proper update after interrupt (1201 ms)
check with useTransition
✓ check 5: updated properly with transition (2320 ms)
✓ check 6: no tearing with transition (70 ms)
✕ check 7: proper branching with transition (5965 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3133 ms)
✕ check 9: no tearing with auto increment (1 ms)
rxjs-hooks
check with events from outside
✓ check 1: updated properly (2212 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1114 ms)
check with useTransition
✓ check 5: updated properly with transition (2463 ms)
✓ check 6: no tearing with transition (42 ms)
✕ check 7: proper branching with transition (5998 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3213 ms)
✓ check 9: no tearing with auto increment (1 ms)
rx-store
check with events from outside
✓ check 1: updated properly (3289 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1242 ms)
check with useTransition
✓ check 5: updated properly with transition (2445 ms)
✓ check 6: no tearing with transition (40 ms)
✕ check 7: proper branching with transition (6116 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3093 ms)
✓ check 9: no tearing with auto increment (1 ms)
klyva
check with events from outside
✓ check 1: updated properly (2258 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1174 ms)
check with useTransition
✓ check 5: updated properly with transition (2396 ms)
✓ check 6: no tearing with transition (143 ms)
✕ check 7: proper branching with transition (6242 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3263 ms)
✕ check 9: no tearing with auto increment (2 ms)
valtio
check with events from outside
✓ check 1: updated properly (2346 ms)
✓ check 2: no tearing during update (1 ms)
✓ check 3: ability to interrupt render
✓ check 4: proper update after interrupt (1178 ms)
check with useTransition
✓ check 5: updated properly with transition (2305 ms)
✓ check 6: no tearing with transition (42 ms)
✕ check 7: proper branching with transition (5960 ms)
check with intensive auto increment
✓ check 8: updated properly with auto increment (3356 ms)
✕ check 9: no tearing with auto increment (2 ms)
Check | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
react-redux | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reactive-react-redux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
react-tracked | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | constate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | zustand | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | react-sweet-state | ✅ | ❌ | ❓ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | storeon | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | react-hooks-global-state | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | use-context-selector (w/ useReducer) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | mobx-react-lite | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | use-subscription (w/ redux) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
Mobx (w/ use-subscription) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
simplux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
react-apollo | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
recoil | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jotai | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effector | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
react-rxjs | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RxDeep | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rxjs-hooks | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rx-store | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
klyva | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
valtio | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
Caution
Tearing may not be an issue depending on app requirements. The test is done in a very limited way. The results might contain unexpected errors.
If you are interested
The reason why I created this is to promote my projects!
The feature of these libraries is not only concurrent mode friendly, but also state usage tracking.