Test tearing and branching in React concurrent rendering
React 18 is coming with a new feature called "concurrent rendering". With global state, there's a theoretical issue called "tearing" that might occur in React concurrent rendering.
Let's test the behavior!
- What is tearing in React 18 WG
- Stack Overflow
- Talk by Mark Erikson
- Talk by Flarnie Marchan
- Some other resources
- Old resources
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.
git clone https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-rendering.git
cd will-this-react-global-state-work-in-concurrent-rendering
yarn install
yarn run build-all
yarn run jest
To automatically run tests and update the README.md on OSX:
yarn jest:json
yarn jest:update
- with useTransition
- test 1: updated properly with transition
- test 2: no tearing with transition
- test 3: ability to interrupt render
- test 4: proper branching with transition
- with intensive auto increment (EXPERIMENTAL)
- test 5: updated properly with auto increment
- test 6: no tearing with auto increment
Raw Output
react-redux
with useTransition
✕ test 1: updated properly with transition (1681 ms)
✕ test 2: no tearing with transition (802 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7180 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (2220 ms)
✕ test 6: no tearing with auto increment (2 ms)
react-tracked
with useTransition
✓ test 1: updated properly with transition (3597 ms)
✓ test 2: no tearing with transition (29 ms)
✓ test 3: ability to interrupt render
✓ test 4: proper branching with transition (5426 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (6136 ms)
✓ test 6: no tearing with auto increment (1 ms)
constate
with useTransition
✓ test 1: updated properly with transition (2845 ms)
✓ test 2: no tearing with transition (25 ms)
✓ test 3: ability to interrupt render
✓ test 4: proper branching with transition (3410 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (3993 ms)
✓ test 6: no tearing with auto increment (1 ms)
zustand
with useTransition
✕ test 1: updated properly with transition (2435 ms)
✕ test 2: no tearing with transition (838 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7192 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (2216 ms)
✕ test 6: no tearing with auto increment (1 ms)
react-hooks-global-state
with useTransition
✓ test 1: updated properly with transition (3421 ms)
✓ test 2: no tearing with transition (27 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7186 ms)
with intensive auto increment
✕ test 5: updated properly with auto increment (13184 ms)
✕ test 6: no tearing with auto increment (4 ms)
use-context-selector
with useTransition
✓ test 1: updated properly with transition (3600 ms)
✓ test 2: no tearing with transition (29 ms)
✓ test 3: ability to interrupt render
✓ test 4: proper branching with transition (5434 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (6130 ms)
✓ test 6: no tearing with auto increment
use-subscription
with useTransition
✓ test 1: updated properly with transition (3566 ms)
✓ test 2: no tearing with transition (122 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7540 ms)
with intensive auto increment
✕ test 5: updated properly with auto increment (13192 ms)
✕ test 6: no tearing with auto increment (2 ms)
react-state
with useTransition
✓ test 1: updated properly with transition (2881 ms)
✓ test 2: no tearing with transition (31 ms)
✓ test 3: ability to interrupt render (1 ms)
✓ test 4: proper branching with transition (3410 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (4012 ms)
✓ test 6: no tearing with auto increment (1 ms)
simplux
with useTransition
✓ test 1: updated properly with transition (2859 ms)
✓ test 2: no tearing with transition (29 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7377 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (4087 ms)
✓ test 6: no tearing with auto increment
apollo-client
with useTransition
✕ test 1: updated properly with transition (4649 ms)
✕ test 2: no tearing with transition (32 ms)
✕ test 3: ability to interrupt render
✕ test 4: proper branching with transition (3525 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (2251 ms)
✕ test 6: no tearing with auto increment (1 ms)
recoil
with useTransition
✕ test 1: updated properly with transition (5670 ms)
✓ test 2: no tearing with transition (37 ms)
✕ test 3: ability to interrupt render (1 ms)
✕ test 4: proper branching with transition (4441 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (3048 ms)
✓ test 6: no tearing with auto increment (1 ms)
jotai
with useTransition
✓ test 1: updated properly with transition (3541 ms)
✓ test 2: no tearing with transition (123 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (8527 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (3230 ms)
✕ test 6: no tearing with auto increment (1 ms)
effector
with useTransition
✕ test 1: updated properly with transition (1605 ms)
✕ test 2: no tearing with transition (842 ms)
✓ test 3: ability to interrupt render (1 ms)
✕ test 4: proper branching with transition (7179 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (2224 ms)
✕ test 6: no tearing with auto increment (2 ms)
react-rxjs
with useTransition
✕ test 1: updated properly with transition (5837 ms)
✓ test 2: no tearing with transition (33 ms)
✕ test 3: ability to interrupt render
✕ test 4: proper branching with transition (4535 ms)
with intensive auto increment
✓ test 5: updated properly with auto increment (3015 ms)
✓ test 6: no tearing with auto increment (1 ms)
valtio
with useTransition
✓ test 1: updated properly with transition (3438 ms)
✓ test 2: no tearing with transition (25 ms)
✓ test 3: ability to interrupt render
✕ test 4: proper branching with transition (7187 ms)
with intensive auto increment
✕ test 5: updated properly with auto increment (13199 ms)
✕ test 6: no tearing with auto increment (5 ms)
Test | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
react-redux | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
react-tracked | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
constate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
zustand | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
react-hooks-global-state | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
use-context-selector (w/ useReducer) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
use-subscription (w/ redux) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
simplux | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
apollo-client | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
recoil | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ |
jotai | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
effector | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
react-rxjs | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ |
valtio | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
- Tearing and state branching may not be an issue depending on app requirements.
- The test is done in a very limited way.
- Passing tests don't guarantee anything.
- The results may not be accurate.
- Do not fully trust the results.
The reason why I created this is to test my projects.
- react-tracked
- use-context-selector
- and so on
This repository is a tool for us to test some of global state libraries. While it is totally fine to use the tool for other libraries under the license, we don't generally accept adding a new library to the repository.
However, we are interested in various approaches. If you have any suggestions feel free to open issues or pull requests. We may consider adding (and removing) libraries. Questions and discussions are also welcome in issues.
For listing global state libraries, we have another repository https://github.com/dai-shi/lets-compare-global-state-with-react-hooks in which we accept contributions. It's recommended to run this tool and we put the result there, possibly a reference link to a PR in this repository or a fork of this repository.