State management is a problem that front-end developers can’t get around. To solve this problem, there are some excellent state management solutions, such as Redux, Recoil, and useContext+useReducer in React. There are so many options that they can be confused. This time I will use these three scenarios to complete the same state management requirement for your reference.

Recoil, for those of you who may be unfamiliar, is Facebook’s own son

State to distinguish

As FAR as I’m concerned, state is actually the management of data. I strongly recommend that you manage interface data and local data separately. In reality, most of the data we store into Redux comes from the interface, which is a bad idea. It is recommended to use React Query for interface state management and use the above scheme for local state rolling. React Query will not be expanded in depth here.

State management requirements

The requirements are simple. There are two pages, home and Aubut, and each page has a counter, and we want to keep the state of the counter.

The online preview

The online preview

If you don’t want to look at the code, you can go to the end and look at the comparison

Redux

The use of Redux is familiar. First create homesglac. ts and aboutslice. ts, then register them in the Store and use them with your own wrapped useDispatch and useSelector.

// ./store/homeSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

const homeSlice = createSlice({
  name: "home".initialState: {
    value: 0,},reducers: {
    setCount: (state, action: PayloadAction<number>) = >{ state.value = action.payload; ,}}});export const { setCount } = homeSlice.actions;

export default homeSlice;
Copy the code

Aboutslice. ts is very similar to homeSlice.

// ./redux/store/index.ts
import { configureStore } from "@reduxjs/toolkit";
import aboutSlice from "./aboutSlice";
import homeSlice from "./homeSlice";

const store = configureStore({
  reducer: {
    home: homeSlice.reducer,
    about: aboutSlice.reducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

export default store;
Copy the code
// ./redux/store/hook.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from ".";

export const useAppDispatch = () = > useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
Copy the code

Redux is inherently deficient and needs to encapsulate a layer of its own to get type checking

// ./redux/Home.tsx
import { useAppDispatch, useAppSelector } from "./store/hooks";
import { setCount } from "./store/homeSlice";

const Home = () = > {
  const count = useAppSelector((state) = > state.home.value);
  const dispatch = useAppDispatch();

  return (
    <div>
      <p>is Home page : {count}</p>
      <button
        onClick={()= > {
          dispatch(setCount(count + 1));
        }}
      >
        add
      </button>
    </div>
  );
};
export default Home;
Copy the code

useContext + useReducer

The idea of this solution is to create the context that you want to share on the upper layer, and then congratulations, useReducer, modify the data

// ./store/HomeContext.ts
import { createContext, Dispatch } from "react";

interface State {
  count: number;
}
export interface InitContextProps {
  state: State;
  setState: Dispatch<Partial<State>>;
}
export const dataInit = {
  count: 0};export const reducer = (
  prevState: State,
  updatedProperty: Partial<State>
): State= >({... prevState, ... updatedProperty, });const HomeContext = createContext<InitContextProps>({
  state: dataInit,
  setState: () = > {
    throw new Error("HomeContext: Outside the scope of homecontext.provider"); }});export default HomeContext;
Copy the code

Create HomeContext

//./context/index.tsx
import React, { useReducer } from "react";
import HomeContext, {
  dataInit as homeDataInit,
  reducer as HomeReducer,
} from "./store/HomeContext";
import AubotContext, {
  dataInit as aubotDataInit,
  reducer as AubotReducer,
} from "./store/AubotContext";
const Context = () = > {
  const [homeState, setHomeState] = useReducer(HomeReducer, homeDataInit);
  const [aubotState, setAubotState] = useReducer(AubotReducer, aubotDataInit);
  return (
    <>
      <HomeContext.Provider
        value={{ state: homeState.setState: setHomeState }}
      >
        <AubotContext.Provider
          value={{ state: aubotState.setState: setAubotState }}
        >.</AubotContext.Provider>
      </HomeContext.Provider>
    </>
  );
};
export default Context;
Copy the code

On top

//./context/Home.tsx
import React, { useContext } from "react";
import HomeContext from "./store/HomeContext";
const Home = () = > {
  const { state, setState } = useContext(HomeContext);
  return (
    <div>
      <p>is Home page : {state.count}</p>
      <button
        onClick={()= > {
          setState({ count: state.count + 1 });
        }}
      >
        add
      </button>
    </div>
  );
};
export default Home;
Copy the code

Recoil

// ./recoil/store.ts
import { atom } from "recoil";

export const homeState = atom({
  key: "homeState".default: 0});Copy the code
// ./recoil/index.ts
import React from "react";
import { RecoilRoot } from "recoil";

const RecoilPage = () = > {
  return (
    <>
      <RecoilRoot>.</RecoilRoot>
    </>
  );
};
export default RecoilPage;
Copy the code

You just need to wrap a layer of RecoilRoot on top of it, which is recommended at the root.

// ./recoil/home.ts
import React from 'react'
import { useRecoilState } from 'recoil';
import { homeState } from './store';

const Home = () = > {
  const [count, setCount] = useRecoilState(homeState);

  return (<div>
    <p>is Home page : {count}</p>
    <button onClick={()= > {
      setCount(count + 1)
    }} >add</button>
  </div>)}export default Home

Copy the code

contrast

Reudx

  • TS support is weak
  • Need to be instoreRegistration. It’s not a drawback, butrecoilNo need, but afraid of comparison.
  • The state is unified management, and it is easy to conflict files when many people cooperate.
  • This requirement requires the most code

useContext + useReducer

  • Root is miao Red, React, no third-party library
  • Every time a new Context is created, it needs to be wrapped on the upper layer, and the upper layer files that have nothing to do with the main logic need to be changed frequently.
  • TS support is good, but for good TS support, template syntax is a bit too much.

recoil

  • TS good support
  • You don’t need to register, just wrap RecoilRoot on the top layer, and it hardly changes later
  • The simplest to use and the least amount of code

I recommend

Recoil >useContext + useReducer>Reudx> Others