above

The main function of React-Redux is to associate the Redux and React components. Use the provided connect method to enable any react component to access the global Store. This is done by storing a store in the context provided by the provider. When connect is called, the props of the component can be replaced so that it can access the customized data or methods.

The target

This article tries to use React-hook to replace the basic functionality of React-Redux.

React-redux features:

  • Maintain a store globally
  • Any component can get a store, and preferably props can be customized.
  • Provide dispatch capability for Actions (mapStateDispatch)

useReducer

Take a look at what the built-in capabilities are, and the examples on the official website will give us some inspiration (and an alternative to useState).

const initialState = {count: 0};

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
      <button onClick={()= > dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Copy the code

It seems that hook actually has the mechanism to use redux, the distribution of state change action, one-way data flow. But hooks do not share state, that is, each time the useReducer keeps the data state independent. Take this example:

function CountWrapper() {
    return (
        <section>
            <Counter initialCount={1} />
            <Counter initialCount={1} />
        </setion>
        )
}
Copy the code

The internal data of the two Count components is independent and cannot affect each other, so state management cannot be mentioned. UseReducer itself is implemented using useState encapsulation.

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);
 
  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }
 
  return [state, dispatch];
}
Copy the code

StorePriovider

UseReducer can not replace the react-Redux global sharing mechanism. In view of the implementation of React-Redux, it provides a Provider that uses context to do so. UseContext is used to remedy this problem

Accepts a context object (the value returned from React.createContext) and returns the current context value, as given by the nearest context provider for the given context. When the provider updates, this Hook will trigger a rerender with the latest context value.

It receives a context object called React. CreateContext. When the provider updates, it passes in store updates, and useContext returns the latest value

import {createContext, useContext} from 'react';
 
const context = createContext(null);
export const StoreProvider = context.provider;
 
const store = useContext(context);

Copy the code

useDispatch

Here we provide a root component to store, and when store updates, we can also use useContext to get the latest value. A hook is exposed to return the dispatch on the store to issue an action to change the state.

export function useDispatch() {
  const store = useContext(Context);
  return store.dispatch;
}
Copy the code

useStoreState

To retrieve the global state, we need to write a custom hook that calls store.getStore().

export function useStoreState(mapState){
    const store = useContext(context);
    return mapState(store.getStore());
}
Copy the code

This takes the state away, but it leaves out the very important issue of how to tell the component to fetch new data again when the data on the Store changes. When the store changes, it’s not associated with the view. Another problem is not looking at mapState changes. For the first problem, we can use the useEffect built-in hook to complete the subscription on the store when the component is mounted and unsubscribe when the component is unmont. Changes to mapState can be monitored using useState and executed to the corresponding setter method each time a change occurs. The following code

export function useStoreState(mapState) {
    const store = useContext(context);
 
    const mapStateFn = () = > mapState(store.getState());
 
    const [mappedState, setMappedState] = useState(() = > mapStateFn());
 
    // If the store or mapState change, rerun mapState
    const [prevStore, setPrevStore] = useState(store);
    const [prevMapState, setPrevMapState] = useState(() = > mapState);
    if(prevStore ! == store || prevMapState ! == mapState) { setPrevStore(store); setPrevMapState(() = > mapState);
        setMappedState(mapStateFn());
    }
 
    const lastRenderedMappedState = useRef();
    // Set the last mapped state after rendering.
    useEffect(() = > {
        lastRenderedMappedState.current = mappedState;
    });
 
    useEffect(
        () = > {
            // Run the mapState callback and if the result has changed, make the
            // component re-render with the new state.
            const checkForUpdates = () = > {
                const newMappedState = mapStateFn();
                if (!shallowEqual(newMappedState, lastRenderedMappedState.current)) {
                    setMappedState(newMappedState);
                }
            };
                        
            // Pull data from the store on first render.
            checkForUpdates();
 
            // Subscribe to the store to be notified of subsequent changes.
            const unsubscribe = store.subscribe(checkForUpdates);
 
            // The return value of useEffect will be called when unmounting, so
            // we use it to unsubscribe from the store.
            return unsubscribe;
        },
        [store, mapState],
    );
    return mappedState
}
Copy the code

React-hook can replace React-Redux by simply encapsulating the react-Redux function. However, in a large Web service scenario, react-Redux optimization strategies and scenarios are considered. This includes inheritance from third parties such as immutable-js, redux-thunk, and redux-saga. Such optimization, if you need to do your own integration implementation may be costly, and can not consider the overall and accurate positioning of large Web services. In addition, the latest React ecosystem generates the React-server-Compoents server-side component rendering technology, which will reduce a large amount of data rendering and data processing costs, and achieve a substantial leap in data maintenance and performance optimization. But do small and medium-sized Web projects still need to use React-Redux? In fact, it is not necessary. The vast majority of small and medium-sized Web systems do not have too many complex interaction scenarios and asynchronous problems. It may only need to use React-DOM and AXIos to complete the development of the whole project.