DEV Community

南小北
南小北

Posted on • Updated on

React State Management in 2022

1. What is "state"?

In the era of jQuery, JS code is mixed with DOM structure. When various processes are complex and intertwined, it forms spaghetti code. When using publish/subscribe model, debugging will be messy.

jQuery is imperative programming for "process", and so many commands are ultimately to update the "data" in the UI. Why not change the data directly?

Beijing → Shanghai, just change city="Beijing" into city="Shanghai". No matter whether the plane or train breaks down on foot, or whether you will meet Wang Baoqiang on the pipeline.

The significance of modern front-end framework is the innovation of problem-solving ideas, which changes various commands of "process" into the description of "state".

What is state? State is the dynamic data in the UI.

2. State in React

React was born in May 2013. But before 2015, it was probably the world of jQuery. React 0.13.0 was released in March 2015, bringing the writing method of class components.

In the era of React class components, the state is this.state, use this.setState to update.

In order to avoid a mess, React introduces the concepts of "component" and "unidirectional data flow". With state and components, there is naturally the transfer of state between components, which is generally called "communication".

Parent-child communication is relatively simple, while the communication of deep-level and long-distance components depends on "lifting state up" + pass props layer by layer.
Therefore, React introduced Context, an official solution to solve the "cross level" communication of components.

However, Context is actually equivalent to "lifting state up". There is no additional performance optimization, and it is wordy.

In order to optimize performance, multiple contexts are usually added, which is more verbose. When the project is not so complex, it is better to pass props layer by layer.

3. What is "state management"?

In terms of pragmatism, "state management" is to solve the "cross level" communication between components.

Of course, when using state management library, it will bring some derived thinking patterns, such as how to organize state, how to split public logic, business logic, component logic, etc. but in the final analysis, these are not the core reasons.

The core is to solve practical problems — communication. Other concepts and philosophies are not necessary.

Context is not so easy to use, and React officials have no best practices, so community libraries were born.

4. State management in class era

The era of React class components is the story of Redux (and its related derivative libraries) and MobX.

Redux is an implementation in line with the concept of React. MobX’s "monitoring" mode is characterized by "not enough React", but it is simple to use.

The pros and cons of Redux have been discussed too much. In short, developers are concerned about "use", while Redux is concerned about "philosophy".

Previously, I joked that in fact, Redux can express it in one line of code, but wrote a sleepy document with paper specifications:

createStore = (reducer, state) => ({ dispatch: (action) => (state = reducer(state, action)) });
Enter fullscreen mode Exit fullscreen mode

The principle of almost all React state managers are actually very simple. An implementation of "observer mode":

Subscribe a listener in each component. When the state is updated, call the listeners again to trigger the component update.

5. Why hooks?

React class component has the following problems:

  1. this.state is an object. Each time a part of state is updated, a new field can also be added, which makes the whole state chaotic.
  2. When using modes such as HOC, the data source in this.props will be opaque and chaotic.
  3. Because of the existence of the magic pointer this, it is easy to hang a lot of things on it and call each other at will, which will entangle the logic.

In order to solve the above problems, React introduces Hooks:

  1. Break up the chaotic state into primitive.
  2. Provide logical sharing to replace HOC.
  3. this no longer exists in the component.

This is an innovation of development concept and organization concept. Hooks has 3 strong characteristics: primitive, decentralization and algebraic effects.

  1. Primitive. Built from the bottom, makes the data structure clearer. At the same time, it is also an engineering trend. e.g. Tailwind CSS is to primitive CSS.
  2. Decentralization. There is a common concept of "top-level distribution" in the class era, but Hooks brings a strong concept of "component autonomy" (for example, Provider is no longer required, the component requests are processed by itself). At the same time, decentralization is also a popular concept in other fields.
  3. Algebraic effects. In the final analysis, Hooks can be understood as a pipeline that connects to the core capabilities of React, and exposes internal machines to developers.

6. State management in Hooks Era

After the emergence of Hooks, the community has not had a state manager that once unified like Redux.

Redux has added some capabilities such as useSelector, useDispatch and useStore, and Facebook also opened a library Recoil.

However, Redux is old-fashioned after all, and the shadow left by it in the early stage is too big. Many people’s thinking is formatted. Writing it casually is in the clouds, just to realize a simple function,

Recoil’s writing rules seem awkward and wordy, and its development is not warm.

// Recoil
atom({ key: 'textState', default: '' });
useRecoilState(textState);
Enter fullscreen mode Exit fullscreen mode

In the Hooks era, a mysterious organization emerged and contributed three state management libraries at one go.

It’s pmndrs, pmndrs for poimandres. pmnd.rs

When it comes to "organization", in fact, the main developer should be one person, the master, Daishi Kato. github.com/dai-shi

The three libraries are zustand, jotai and valtio. Interestingly, these three words actually mean "state".

zustand 🇩🇪 German "state", jotai 🇯🇵 Japanese "status", valtio 🇫🇮 Finnish "state".

Take a brief look at the usage:

// zustand 🇩🇪 - Redux spirit, old times, centralized
const useStore = create((set) => ({
  bears: 0,
  removeBears: () => set({ bears: 0 }),
}));
const bears = useStore((state) => state.bears);
Enter fullscreen mode Exit fullscreen mode
// jotai 🇯🇵 - primitive concept, a little wordy, Hooks spirit
const countAtom = atom(0);
const [count, setCount] = useAtom(countAtom);
Enter fullscreen mode Exit fullscreen mode
// valtio 🇫🇮 - proxy concept, "not very react", simple to use
const state = proxy({ count: 0, text: 'hello' });
const snap = useSnapshot(state);
Enter fullscreen mode Exit fullscreen mode

7. Greedy update vs lazy update?

As mentioned earlier when referring to MobX, the scheme of proxy "monitoring" is not React, but it is practical, simple and most intuitive.

In essence, React is a "greedy update" strategy, full re-render then diff.

Proxy is a "lazy update" strategy, which can accurately know which variable is updated. Therefore, using proxy, we can optimize the performance of re-render.

React Forget introduced on React conf represents that React itself does not exclude some optimization in the idea of "lazy update".

Note that the above words "greedy update" and "lazy update" are my own words, referring to the concepts of greedy and lazy in regular expression.

8. Change of React state management ideas

  1. All state in a large object → split into primitive
  2. Opaque data → transparent data
  3. Top level request, issue data → the component request by itself
  4. State promotion → component autonomy
  5. Provider & container components → just Hooks
  6. Chaotic set → transparent decoupling
  7. Greedy update → lazy update
  8. Big and complete, strong concept, DX ❌ → clearer, simpler, DX ✅
  9. Less concept, more intuition
  10. Less rules, more automation

Generally speaking, although this is a change in state management, it is also a change in React community development, a continuous exploration of best practices:

  1. Centralization → decentralization
  2. Data collection → primitive
  3. Build a structure, completely from the ground up

9. Introducing resso, the simplest React state manager

I have been thinking about what kind of React state manager is the simplest to use, and constantly explore a tool that is the most comfortable to use.

Retalk (Redux best practice) and flooks (hooks state management) have been developed before, but with the emergence of new ideas, some of the latest inspiration is now concentrated in the state management library of resso.

Here is resso:

import resso from 'resso';

const store = resso({ count: 0, text: 'hello' });

function App() {
  const { count } = store;
  return (
    <>
      {count}
      <button onClick={() => store.count++}>+</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/nanxiaobei/resso

Note that it is easier to write than the very simple valtio. It should not be simpler. If so, please let me know.

More importantly, resso will automatically optimize re-render, and never trigger additional re-render just because the data is in the same object.

In fact, state management is a very simple thing, but Redux and other tools add too much complexity. The original purpose of people using a tool is to solve problems.

So, simple and clear, let the tool return to the tool. The way we know a hammer is to pick it up and use it.

I hope resso will be liked by those who need it.

10. Invest the future

But what’s the use of all this?

When new things keep coming, people will inevitably ask: class components are not unusable, Redux is not unusable, or more thoroughly, jQuery is not unusable. Why do you have to chase these new things?

An abstract explanation: we should constantly invest in the future.

This is not only in the development, in the work, but also in any field — "in the form of continuous subdivision in the new track, exchange the first identity for resources."
The track of the old world is crowded with hard-working travelers. Although the new world is a mirage, only the new world will jump everything.


The above content comes from the share of React State Management in 2022.

PDF download
Keynote download (more animation~)

Top comments (2)

Collapse
 
phryneas profile image
Info Comment hidden by post author - thread only accessible via permalink
Lenz Weber

It is incredibly easy to break that library, as you can see in this codeSandbox: codesandbox.io/s/patient-waterfall...

const snap = resso({ count: 0, text: "hello" });

export default function App() {
  if (snap.count > 2) {
    /*
   as `snap.count` is accessed twice now, this breaks the rules of hooks:
   Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useMemo                    useMemo
3. useEffect                  useEffect
4. useState                   useState
5. useMemo                    useMemo
6. useEffect                  useEffect
7. undefined                  useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    */
    return (
      <>
        now state {snap.count} is bigger than 2: {snap.count}
      </>
    );
  }
  return (
    <>
      {snap.count}
      <button onClick={() => snap.count++}>+</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nanxiaobei profile image
南小北 • Edited

Thanks for your testing!

Since need to let the component know which state is used, so for all situations, better deconstructing the state first and then use it.

fixed demo: codesandbox.io/s/resso-correct-usa...

function App() {
  const { count } = snap;

  if (count > 2) {
    return (
      <>
        now state {count} is bigger than 2: {count}
      </>
    );
  }

  return (
    <>
      {count}
      <button onClick={() => snap.count++}>+</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

And the usage code in resso's README has beed updated.

Some comments have been hidden by the post's author - find out more