1. The background

Every tool, every technology is created to solve one or more problems. React, for example, has no global state:

  • Data sharing
  • Component global communication

State management can be introduced when you have trouble understanding these two issues

2. useContext + useReduce

React Hooks provides two hooks apis:

  1. useContext
  2. useReduce

UseContext solves the data sharing problem by injecting shared data into the top-level component, which can be used by any of its children.

In the returned [state, Dispatch] provided by useReduce, the data state can be modified by Dispatch. Component A can modify the state, and component B can obtain the modified state to solve the global communication problem of components.

Method of use

Thinking (in the Store component) :

  1. const [state, dispatch] = useReducer(reducer, initialState);Use the useReducer to return a state(the data that needs to be shared) and a dispatch (the method to modify the data).
  2. throughconst Context = createContext();To create a Context object.
  3. useContext.ProviderThe package requires top-level components that share data and inject datastateanddispatchMethods.
  4. throughuseContext(Context)To get the shared data. Yesexport useContext(Context)Make the shared data available to other child components via import.

The code structure is:

<Store>
    <Parent store={[state, dispatch]} >// Store is the injected data<ChildA>
        <ChildB>
    </Parent>
</Store>
Copy the code

The complete code is shown below:

1. Store.jsx

import React, { createContext, useReducer, useContext } from 'react';

const initialState = {count: 10};

const reducer = (state, action) = > {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        default:
            throw new Error();
    }
}

Step 2 Create a Context
const Context = createContext();
// Step 4 get the data and export it at the end
const useStore = () = > useContext(Context);


const StoreProvider = props= > {
    // step 1, useReducer returns state, dispatch
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        // Step 3 inject data
        <Context.Provider value={[state, dispatch]} >
            {props.children}
        </Context.Provider>
    );
}

export { useStore, StoreProvider };
Copy the code

2. Parent.jsx

import React from 'react';
import { StoreProvider } from './store';
import ChildA from './ChildA';
import ChildB from './ChildB';

const Parent = () = > {
    return (
    <div>
       <p>This is implemented by react hooks in createContext, useReducer, useContext</p>
       <StoreProvider>
            <ChildA />
            <ChildB />
       </StoreProvider>
    </div>
    );
}

export default Parent;
Copy the code

3. ChildA.jsx

import React from 'react';
import { useStore } from './store.js';

const ChildA = () = > {
    const [, dispatch] = useStore();
    
    return (
        <>
            <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
            <button onClick={()= > dispatch({type: 'increment'})}>+</button>
        </>
    );
}

export default ChildA;
Copy the code

4. ChildB.jsx

import React from 'react';
import { useStore } from './store';

const ChildB = () = > {
    const [state] = useStore();
    return <p>{state.count}</p>;
}

export default ChildB;
Copy the code

3. hox

Hox was developed by Ant Financial Experience Technologies and is touted as the next generation React state manager, fully embracing React Hooks, with the following features

  • Just one API, easy to get started
  • Perfect supportTypeScript
  • usecustom HooksTo define model, perfect for ReactHooks

use

Ideas:

  1. defineModelUse:createModel()Wrap any onecustom hook(here isuseCounter)
  2. useModelThrough:useCounterModel()Data is obtained successfully.

1. Store.js(define Store)

import { useState } from "react";
import { createModel } from "hox";

const useCounter = initialValue= > {
    const [count, setCount] = useState(initialValue ?? 10);
    const decrement = () = > setCount(count - 1);
    const increment = () = > setCount(count + 1);
    return {
        count,
        decrement,
        increment
    };
}
// Any custom Hook wrapped in createModel becomes persistent and globally shared data. It is still a Custom Hook after packaging
// createModel(useCounter) equals const useCounterModel = createModel(useCounter);
export default createModel(useCounter);
// with initial value
// export default createModel(useCounter, 20);
Copy the code

2. Counter. JSX (using store)

import React from 'react';
// useCounterModel is a real Hook that subscribs to data updates. When the "+" button is clicked, an update to the Model is triggered and all components or hooks that use the useCounterModel are eventually notified.
import useCounterModel from "./model";

const Counter = () = > {
  // The createModel return value is a Hook, which can be used in the same way as React Hooks
  const counter = useCounterModel();
  return (
    <div>
      <p>{counter.count}</p>
      <button onClick={counter.increment}>+</button>
      <button onClick={counter.decrement}>-</button>
    </div>
  );
};
export default Counter;
Copy the code

3. mobx-state-tree

Mobx-state-tree is a state management tool developed based on MOBx. The core idea is to generate Model through types. Model can define an object structure, and a type can nest multiple models, thus generating a dynamic tree. It can be understood that the dynamic tree corresponds to the data structure of an object

use

Ideas:

  1. throughtypesTo generate acounterStoretheModel
  2. throughcounterStore.create()Generate an instance
  3. observerEmbellishment needs to usestoreComponent, throughpropsTo get the data

The complete code is as follows:

1. Store.js (define store)

import React, { createContext, useContext } from 'react';
import {types} from 'mobx-state-tree';
console.log('types:', types);
const CounterStore = types
    .model('counterStore', {
        count: types.number
    })
    .actions(self= > ({
        increment() {
            self.count++;
        },
        decrement(){ self.count--; }}));export default CounterStore.create({count: 10});
Copy the code

2. Counter.js (using store)

import React from 'react';
import {observer} from 'mobx-react';

// An observer is required to obtain the updated value
const Counter = observer(props= > {
    // The component is used to pass values
    const {store} = props;

    return (
        <div>
            <p>{store.count}</p>
            <button onClick={store.increment}>+</button>
            <button onClick={store.decrement}>-</button>
        </div>
    );
});
export default Counter;
Copy the code

5. How to decide which option to choose

First of all, the three state management schemes can basically solve the two problems proposed at the beginning of the article, so they can be selected in the following order:

  1. Simple projects do not require a state management solution and can use state promotion
  2. usehox, easy to use, fithooks
  3. useuseRedux + usecontext, provided by the government, the difficulty is higher thanhoxBut it’s easy
  4. usemobx, it is difficult to get started

Reference Resources:

  • The React website
  • Hox – The next generation React state manager
  • Mobx – state – tree’s official website