It has been a little over a year since the launch of Hooks

At present, there are roughly two kinds of data flow schemes

  • Based on theReactThe flow of data
  • Is not based onReactThe flow of data

React based data stream implementation

In short, you use official Hooks, such as the most common useState, which are the ones we use the most

useState

UseState allows you to split the state value of a former class component into multiple values

function App() {
  const [state, setState] = useState({ foo: 0 });
  const [loading, setLoading] = useState(false);
  // ...
}
Copy the code

The biggest advantage of using useState is simplicity and clarity, but I think there are two disadvantages

  1. The downside is splitting too much, using too muchuseStateBad management after
  2. If one of thestateValues are too complex and merging when changing values is difficult to handle

There is also another hooks for these two minor problems

useReducer

UseReducer can be said to be the official simplified redux

const reducer = (state, { type, loading }) = > {
  if (type === "FOO") return { ...state, foo: 1 };
  if (type === "SET_LOADING") return { ...state, loading };
  return state;
};
function App() {
  const [state, dispatch] = useReducer(reducer, { foo: 0.loading: false });
  // ...
}
Copy the code

A minor difference from Redux is that React advocates assigning the initial value to the useReudcer parameter instead of the Reducer state

The above two methods are more convenient to use in a single component or parent-child component. What if you want to share data without hierarchy like Redux? The corresponding methods are also provided in Hooks

useContext + useReducer

This is the most common way to share state across components that I’ve used all year

const Context = createContext({});
const reducer = (state, { type, loading }) = > {
  if (type === "FOO") return { ...state, foo: 1 };
  if (type === "SET_LOADING") return { ...state, loading };
  return state;
};
function App() {
  const [state, dispatch] = useReducer(reducer, { foo: 0.loading: false });
  return (
    <Context.Provider value={{ state.dispatch}} >/ /... children</Context.Provider>
  );
}
function Foo() {
  const { state, dispatch } = useContext(Context);
  // ...
}
Copy the code

The only downside is that you need to use createContext to create the Context and mount the Provider, which is a bit more step. You also need to manage the Context yourself, which is purely manual

unstated-next

Unstated -next is a 200 byte state management solution

function useCounter(initialState = 0) {
  const [count, setCount] = useState(initialState)
  return { count, setCount }
}

const Counter = createContainer(useCounter);

function Foo() {
  const counter = Counter.useContainer();
  // ...
}

function App() {
  return (
    <Counter.Provider>
      <Foo />
    </Counter.Provider>)}Copy the code

The implementation of unstated-next uses all React apis and has short source code. The source code will be analyzed below

Using unstated-next eliminates the need to create and manage the Context ourselves, switching from purely manual to semi-automatic

The useModel provided in UmiJS

UmiJS provides a useModel method that makes it easy to use global hooks. Its default rule is that hooks exported from SRC /models are applied globally

// src/models/count.ts
export default() = > {const [count, setCount] = useState(0);
  return { count, setCount };
};
// 
function App() {
  const { count } = useModel('count');
  // ...
}
Copy the code

When you use the React Developer Tools, you can see that there is a Provider in the outermost layer that contains values that export hooks. I think it could be achieved in a similar way to unstated-next

From creating and managing your own Context, mounting your own Provider, to mounting your own Provder, to writing only hooks logic, the process is manual — semi-automatic — automatic

The disadvantage is that it is limited in scope and is limited to the UmiJS framework

Pros and cons of React-based data streams

Only personal summary during use

advantages

  • Based on theReact, no additional learning process, easy to use

disadvantages

  • Precise refresh is not possible
  • It is simply data flow management and does not involve the usual asynchronous data processing

Context cannot be accurately refreshed

Data is a whole and cannot be accurately refreshed. React will automatically trigger refresh once it changes

function Foo() {
  const { state: { foo } } = useContext(Context);
  // ...
}
function Bar() {
  const { state: { bar } } = useContext(Context);
  // ...
}
Copy the code

If a change is made to state.foo by calling Dispatch () in

,

is also refreshed

Data stream implementation not based on React

The data stream implementation that is not based on React is that the data storage is not in React. Data changes will not directly trigger component refresh, but trigger component re-rendering through other ways. I have only used two of them

  • Redux+React-Redux
  • DvaJs

React-Redux

After the Hooks were introduced, react-redux also updated the Hooks methods to use useSelector() to get state

const Counter = (a)= > {
  const counter = useSelector(state= > state.counter)
  // ...
}
Copy the code

It has the advantage that it will refresh precisely and will not cause a whole refresh like Context, since the useSelector rerenders itself rather than handing it over to React

The drawback of Redux+React-Redux is that I need to manage a lot of files

DvaJS

DvaJSIt doesn’t actually provideHooksData flow mode

DvaJS is rarely used alone. UmiJS is basically used. In fact, there are Hooks in Umi to get data

Yunqian big guy may be devoted to UmiJS development, has not updated for a long time, but I think as a very high level of integration of excellent data flow management.

My summary of DvaJS after only 2 months of use

advantages

  • High level of integration, about the form, useredux-sageSolve the asynchronous data flow problem
  • thanredux+react-reduxMuch simpler. No more file management problems
  • Dynamic increase of the compactreducer, allowing some data to be lazily loaded

A little summary

In the era of class components, there was no way to split states, class components felt heavy, component level states were more difficult to manage, Context popularity was not high, using

made components heavier, and later with contextType it was not as concise as useContext. So it feels like Redux was popular for a reason, because React itself doesn’t provide good state management

During the days of Hooks, everything became much simpler. UseReducer was an official version of Redux. State was also simpler at the component level. The useContext+useReducer solution solves most scenarios where shared state is required

I have been using React for a long time, and I feel that many scenarios do not share the global state. The project I am working on now is the background management system, and the page data is not associated with other pages. I use Context only, so I prefer the lightweight Hooks data flow solution

Look at the source code for unstated-next

Sometimes feel really become a porter, the lack of their own ideas, is very dull, mentioned above, I have been using useContext for a long time to share the state, each time is manual, a few days ago suddenly thought, why do you want to do repeated work, a search is really big guy has written

// ProviderProps export interface ContainerProviderProps<State = void> {initialState? : State; children: React.ReactNode; } // createContainer createContainer type export interface Container<Value, State = void> {Provider: React.ComponentType<ContainerProviderProps<State>>; useContainer: () => Value; } Container export function createContainer<Value, State = void>(// customize data hook useHook: (initialState? : State) = > Value) : the Container < Value, State > {/ / the Context used to pass data to let the Context. = the React createContext Value | null < > (null); Function Provider(props: ContainerProviderProps<State>) {let value = useHook(props. InitialState); Return < context. Provider value={value}>{props. Children}</ context. Provider>; } function useContainer(): Value {let Value = react.usecontext (Context); if (value === null) { throw new Error("Component must be wrapped with <Container.Provider>"); } return value; } return { Provider, useContainer }; } export function useContainer<Value, State = void>( container: Container<Value, State> ): Value { return container.useContainer(); }Copy the code

The source code is simple, just a closure

When the authors say things like “I believe React is already very good at state management” and “I wish the community would abandon a state management library like Redux and find a better way to use React’s built-in toolchain,” I think they make a lot of sense

Although I feel that this article does not have any technical content, but no inspiration, and have not shared an article for three weeks


Welcome to follow my public account ~


Reference article: Intensive reading of the React Hooks Data Stream