Skip to content
Checking tearing in React concurrent mode
JavaScript HTML
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
__tests__ make app and spec a little more readable, maybe. Aug 11, 2019
public
src
.eslintrc.json
.gitignore better app and better checks Jul 20, 2019
.travis.yml
README.md make app and spec a little more readable, maybe. Aug 11, 2019
jest-puppeteer.config.js
package-lock.json
package.json
webpack.config.js better app and better checks Jul 20, 2019

README.md

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?

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
npm install
npm run build-all
PORT=8080 npm run http-server &
PORT=8080 npm 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

Preview

Result

Raw Output
  react-redux
    ✓ check1: updated properly (3228ms)
    ✕ check2: no tearing during update (23ms)
    ✓ check3: ability to interrupt render
    ✕ check4: proper update after interrupt (5107ms)
  reactive-react-redux
    ✓ check1: updated properly (3187ms)
    ✓ check2: no tearing during update (2ms)
    ✓ check3: ability to interrupt render
    ✓ check4: proper update after interrupt (1159ms)
  react-tracked
    ✓ check1: updated properly (8297ms)
    ✓ check2: no tearing during update
    ✓ check3: ability to interrupt render (1ms)
    ✓ check4: proper update after interrupt (1165ms)
  constate
    ✓ check1: updated properly (8339ms)
    ✓ check2: no tearing during update (1ms)
    ✓ check3: ability to interrupt render
    ✓ check4: proper update after interrupt (2226ms)
  unstated-next
    ✓ check1: updated properly (8284ms)
    ✓ check2: no tearing during update (1ms)
    ✓ check3: ability to interrupt render
    ✓ check4: proper update after interrupt (1125ms)
  zustand
    ✓ check1: updated properly (4252ms)
    ✕ check2: no tearing during update (21ms)
    ✓ check3: ability to interrupt render
    ✕ check4: proper update after interrupt (5104ms)
  react-sweet-state
    ✓ check1: updated properly (8559ms)
    ✕ check2: no tearing during update (1ms)
    ✓ check3: ability to interrupt render (1ms)
    ✕ check4: proper update after interrupt (5029ms)
  storeon
    ✓ check1: updated properly (3178ms)
    ✕ check2: no tearing during update (20ms)
    ✓ check3: ability to interrupt render
    ✕ check4: proper update after interrupt (5104ms)
  react-hooks-global-state
    ✓ check1: updated properly (8312ms)
    ✓ check2: no tearing during update (2ms)
    ✓ check3: ability to interrupt render
    ✓ check4: proper update after interrupt (1134ms)
  use-context-selector
    ✓ check1: updated properly (8635ms)
    ✓ check2: no tearing during update (1ms)
    ✓ check3: ability to interrupt render
    ✓ check4: proper update after interrupt (1125ms)
  mobx-react-lite
    ✕ check1: updated properly (11590ms)
    ✕ check2: no tearing during update (2ms)
    ✓ check3: ability to interrupt render
    ✕ check4: proper update after interrupt (5031ms)
  use-subscription
    ✓ check1: updated properly (3240ms)
    ✕ check2: no tearing during update (22ms)
    ✓ check3: ability to interrupt render (1ms)
    ✕ check4: proper update after interrupt (5116ms)
check1: updated properly check2: no tearing during update check3: ability to interrupt render check4: proper update after interrupt
react-redux Pass Fail Pass Fail
reactive-react-redux Pass Pass Pass Pass
react-tracked Pass Pass Pass Pass
constate Pass Pass Pass Pass
unstated-next Pass Pass Pass Pass
zustand Pass Fail Pass Fail
react-sweet-state Pass Fail Pass Fail
storeon Pass Fail Pass Fail
react-hooks-global-state Pass Pass Pass Pass
use-context-selector Pass Pass Pass Pass
mobx-react-lite Fail Fail Pass Fail
use-subscription (w/ redux) Pass Fail Pass Fail

Caution

Do no believe the result too much. The test is done in a very limited way. Something might be wrong.

Caution2

We are not yet sure what the final conurrent mode would be. It's likely that there could be some issues other than "tearing."

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.

You can’t perform that action at this time.