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

Test tearing and branching in React concurrent rendering

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Library: react-ridge-state

RichardLindhout opened this issue · comments

I'm trying out my own state library it works great so far, do not see any error logs! Only problem is does not have a reducer but I created my own dispatch function.

Do you think the following is suitable in comparison to other libraries?

import React, { unstable_useTransition as useTransition } from 'react';
import {
  newRidgeState,
  useRidgeState,
  getRidgeState,
  setRidgeState,
} from 'react-ridge-state';

import {
  syncBlock,
  useRegisterIncrementDispatcher,
  ids,
  useCheckTearing,
  initialState,
  reducer,
} from '../common';

const globalState = newRidgeState(initialState);

const dispatch = (action) => {
  const old = getRidgeState(globalState);
  setRidgeState(globalState, reducer(old, action));
};

const Counter = React.memo(() => {
  const [count] = useRidgeState(globalState);
  syncBlock();
  return <div className="count">{count.count}</div>;
});

const Main = () => {
  const [state] = useRidgeState(globalState);
  useCheckTearing();
  useRegisterIncrementDispatcher(
    React.useCallback(() => {
      dispatch({ type: 'increment' });
    }, [])
  );
  const [localCount, localIncrement] = React.useReducer((c) => c + 1, 0);
  const normalIncrement = () => {
    dispatch({ type: 'increment' });
  };
  const [startTransition, isPending] = useTransition();
  const transitionIncrement = () => {
    startTransition(() => {
      dispatch({ type: 'increment' });
    });
  };

  return (
    <div>
      REACT_RIDGE_STATE
      <button type="button" id="normalIncrement" onClick={normalIncrement}>
        Increment shared count normally (two clicks to increment one)
      </button>
      <button
        type="button"
        id="transitionIncrement"
        onClick={transitionIncrement}
      >
        Increment shared count in transition (two clicks to increment one)
      </button>
      <span id="pending">{isPending && 'Pending...'}</span>
      <h1>Shared Count</h1>
      {ids.map((id) => (
        <Counter key={id} />
      ))}
      <div className="count">{state.count}</div>
      <h1>Local Count</h1>
      {localCount}
      <button type="button" id="localIncrement" onClick={localIncrement}>
        Increment local count
      </button>
    </div>
  );
};

const App = () => <Main />;

export default App;

Looks good. It's close to the case of Recoil, which I added lately. Please check that out and compare with it.

https://github.com/web-ridge/react-ridge-state/blob/master/src/index.ts
Just had a quick look. Small and clean. Nice. If I understand it correctly, it suffers from the tearing issue. As I keep saying, that doesn't mean really bad, but what can be done with it is to use use-subscription now or wait for useMutableSource.

Numbers are not different on my laptop while they are with recoil but I copied recoil and changed it to my state library but had difficulties to mimick same thing because I don't have a custom get or set or dispatch.

Thanks btw! ;-)!

API is Influenced by Recoil, I created library after announcement because we need my state sometimes outside of a React component. I was just curious how this test would work out but as far as I did run it on my laptop it did not have failings or miscountings I did not expect this I thought it would have issues with concurrent mode but I will try to get it closer to the recoil example

What I would expect is you should get the same result like zustand and recoil:

check 1: pass
check 2: fail
check 3: pass
check 4: pass
check 5: pass
check 6: fail
check 7: fail
check 8: pass
check 9: fail

I like the Recoil API too (but its current implementation seems too messy.)

My react-hooks-global-state has somewhat close API, but it doesn't allow creating infinite number of states. (Hm, I might change it in v2.)

I will check later I could not get the test working. Some node errors I need to upgrade node I think.

I like react-hooks-global-state! I did not know it existed. I like that you can set the state outside of React!

I wasn't so sure if it's the right way when I started, but as of v2 it uses upcoming useMutableSource which supports external source explicitly. I'm not sure which path Recoil will take.

Not global, they'll only support it inside a React context they said in an issue. But maybe I did not understand

I will check that issue later. Been there done that. It's extremely tricky to keep all states in React and provide Recoil API (not impossible though).

For the other path (= not global), I developed react-tracked.

facebookexperimental/Recoil#5

Recoil state is not stored globally anywhere, but is specific to a particular React context.

Alright. I assume this mean "React state" not "React context".

Not only that, but it's specific to a particular rendered tree (for concurrent mode, coming soon).

I'm not sure if I follow this one.

Anyhow, their goal is almost clear. I misunderstood it because they are considering the use of useMutableSource. (It's not very compatible with context. Unless abusing it, like mine.)

And, if Recoil is properly implemented with React state, it will pass all the check in this tool (check 1 - 9).

Thanks for making these test cases! This is going to be super useful as we test our two candidate approaches to CM.

(Looking forward to hearing about the two approaches in detail.)

Closing this as stale.