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 the
React
The flow of data - Is not based on
React
The 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
- The downside is splitting too much, using too much
useState
Bad management after - If one of the
state
Values 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 the
React
, 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
DvaJS
It doesn’t actually provideHooks
Data 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, use
redux-sage
Solve the asynchronous data flow problem - than
redux
+react-redux
Much simpler. No more file management problems - Dynamic increase of the compact
reducer
, 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