/will-this-react-global-state-work-in-concurrent-rendering

Test tearing and branching in React concurrent rendering

Primary LanguageJavaScriptMIT LicenseMIT

Will this React global state work in concurrent rendering?

Test tearing and branching in React concurrent rendering

Caveat: These tests are originally designed for obsolete concurrent mode. We should revisit them for new concurrent rendering feature.

Introduction

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?

What is branching?

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-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

Screencast (old)

Preview

Test scenario

  • Level 1
    • test 1: updated properly with transition
  • Level 2
    • test 2: no tearing with transition
    • test 5: updated properly with auto increment (EXPERIMENTAL)
    • test 6: no tearing with auto increment (EXPERIMENTAL)
  • Level 3
    • test 3: ability to interrupt render
    • test 4: proper branching with transition

Results

Raw Output
 react-redux
   with useTransition
     ✓ test 1: updated properly with transition (3953 ms)
     ✓ test 2: no tearing with transition (24 ms)
     ✕ test 3: ability to interrupt render (1 ms)
     ✕ test 4: proper branching with transition (4512 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (2210 ms)
     ✕ test 6: no tearing with auto increment (2 ms)
 react-tracked
   with useTransition
     ✓ test 1: updated properly with transition (3649 ms)
     ✓ test 2: no tearing with transition (27 ms)
     ✓ test 3: ability to interrupt render
     ✓ test 4: proper branching with transition (5472 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (6114 ms)
     ✓ test 6: no tearing with auto increment
 constate
   with useTransition
     ✓ test 1: updated properly with transition (2652 ms)
     ✓ test 2: no tearing with transition (25 ms)
     ✓ test 3: ability to interrupt render
     ✓ test 4: proper branching with transition (3430 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (4003 ms)
     ✓ test 6: no tearing with auto increment (1 ms)
 zustand
   with useTransition
     ✓ test 1: updated properly with transition (3938 ms)
     ✓ test 2: no tearing with transition (27 ms)
     ✕ test 3: ability to interrupt render (1 ms)
     ✕ test 4: proper branching with transition (4520 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (2202 ms)
     ✕ test 6: no tearing with auto increment (2 ms)
 react-hooks-global-state
   with useTransition
     ✓ test 1: updated properly with transition (3526 ms)
     ✓ test 2: no tearing with transition (25 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (7247 ms)
   with intensive auto increment
     ✕ test 5: updated properly with auto increment (13203 ms)
     ✕ test 6: no tearing with auto increment (4 ms)
 use-context-selector
   with useTransition
     ✓ test 1: updated properly with transition (3677 ms)
     ✓ test 2: no tearing with transition (26 ms)
     ✓ test 3: ability to interrupt render
     ✓ test 4: proper branching with transition (5460 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (6138 ms)
     ✓ test 6: no tearing with auto increment (1 ms)
 use-subscription
   with useTransition
     ✓ test 1: updated properly with transition (3560 ms)
     ✓ test 2: no tearing with transition (120 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (7571 ms)
   with intensive auto increment
     ✕ test 5: updated properly with auto increment (13202 ms)
     ✕ test 6: no tearing with auto increment (9 ms)
 react-state
   with useTransition
     ✓ test 1: updated properly with transition (2643 ms)
     ✓ test 2: no tearing with transition (27 ms)
     ✓ test 3: ability to interrupt render
     ✓ test 4: proper branching with transition (3453 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (4021 ms)
     ✓ test 6: no tearing with auto increment (4 ms)
 simplux
   with useTransition
     ✓ test 1: updated properly with transition (2671 ms)
     ✓ test 2: no tearing with transition (28 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (7411 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (4102 ms)
     ✓ test 6: no tearing with auto increment (2 ms)
 apollo-client
   with useTransition
     ✓ test 1: updated properly with transition (3645 ms)
     ✕ test 2: no tearing with transition (31 ms)
     ✕ test 3: ability to interrupt render (1 ms)
     ✕ test 4: proper branching with transition (3626 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (2260 ms)
     ✕ test 6: no tearing with auto increment (1 ms)
 recoil
   with useTransition
     ✓ test 1: updated properly with transition (3626 ms)
     ✓ test 2: no tearing with transition (26 ms)
     ✕ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (4375 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (3035 ms)
     ✓ test 6: no tearing with auto increment (1 ms)
 jotai
   with useTransition
     ✓ test 1: updated properly with transition (3682 ms)
     ✓ test 2: no tearing with transition (24 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (8401 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (5206 ms)
     ✓ test 6: no tearing with auto increment (1 ms)
 effector
   with useTransition
     ✓ test 1: updated properly with transition (2492 ms)
     ✕ test 2: no tearing with transition (25 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (981 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (2221 ms)
     ✕ test 6: no tearing with auto increment (1 ms)
 react-rxjs
   with useTransition
     ✓ test 1: updated properly with transition (3933 ms)
     ✓ test 2: no tearing with transition (33 ms)
     ✕ test 3: ability to interrupt render (1 ms)
     ✕ test 4: proper branching with transition (4532 ms)
   with intensive auto increment
     ✓ test 5: updated properly with auto increment (3008 ms)
     ✓ test 6: no tearing with auto increment
 valtio
   with useTransition
     ✓ test 1: updated properly with transition (3533 ms)
     ✓ test 2: no tearing with transition (24 ms)
     ✓ test 3: ability to interrupt render
     ✕ test 4: proper branching with transition (7250 ms)
   with intensive auto increment
     ✕ test 5: updated properly with auto increment (13210 ms)
     ✕ test 6: no tearing with auto increment (3 ms)

Test123456
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

Caveats

  • 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.

If you are interested

The reason why I created this is to test my projects.

Contributing

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.