1. Introduction to Redux

Redux is a JavaScript application state container that provides predictable state management.

Allows you to build consistent applications that run in different environments (client, server, native) and are easy to test. Not only that, but it also offers a great development experience, such as a time travel debugger that allows you to preview in real time after editing.

2. Why and when should you use redux

Someone once said that.

“If you don’t know if you need a Redux, you don’t need it.”

“Added Dan Abramov, Redux’s creator.

“You only need a Redux if you have a problem that React really can’t solve.”

If an application isn’t complex enough to manage data using React’s internal state, there’s no need to use Redux.

Because every time you write code using Redux, you have to write extra code and you may need to create more files.

So when is a good time to use redux?

  1. The component tree is large, and different component nodes need to share state
  2. A component needs to change the global state or change the state of another component
  3. There is more data interaction with the back-end server, and the component view depends on the data returned by the back-end

What are the benefits of using Redux?

  1. Redux makes changes in state predictable, since it can only be changed by dispatching an action, which is monitored, and in a development environment, with redux-Devtools, time travel, recording, replay, etc
  2. Redux unified state management, will make the code more regular, easy to maintain and management.

3. Redux basics

3.1 redux API

  • Top-level exposed API
    • createStore(reducer, [preloadedState], [enhancer])
    • combineReducers(reducers)
    • applyMiddleware(… middlewares)
    • bindActionCreators(actionCreators, dispatch)
    • compose(… functions)
  • Store API
    • getState()
    • dispatch(action)
    • subscribe(listener)
    • replaceReducer(nextReducer)

3.1.1 createStore

The createStore function is used to create a store instance. When creating a store, the first parameter is passed to the reducer function. The store stores the state data, and the reducer function is passed to the reducer that defines the rules to modify state.

3.1.2 store. GetState

Store.getsate () returns the state data inside the store. The state data cannot be accessed directly from the outside and must be obtained through the getState method

3.1.3 store. Dispatch

Store.dispatch () dispatches an Action. An action is an object that contains at least one type field. For example: {type: ‘add’}, can contain other data except the type field. Type is used to match the modification rules in the reducer, and other data is used to update the state.

To modify data in state, you must go through Dispatch

3.1.4 store. The subscribe

Subscribe is used to subscribe to changes in the store. The listening function is executed when the dispatch is called

3.2 redux flow

Redux data streams in conjunction with React:

  1. Called in the React componentdispatch(action)And distribute oneaction.actionIs a common object that describes what happens. For example:{ type: 'add', value: 1 }, this action object can be interpreted as “increment the value by 1”
  2. storeGet to theactionAfter that, the build is invokedstoreThe incoming toreducerFunction, and pass the currentstateaction
  3. reducerGet to thestateactionAfter, will passaction.typeMatch to modifystateAnd then modify and return the new onestate
  4. Store saves the new state, and then all subscriptionsstore.subscribe()All listeners are called and passedstore.getState()Get the new state and rerender the component.

4. Use redux

4.1 reducer is introduced

When creating a store, you need to pass in a Reducer function.

reducer = (state, action) => {}

The Reducer (also called reducing function) function takes two arguments:

  • State: The result of the previous accumulation and the value currently being accumulated

  • Action: An object that describes what happens, usually with a type field. For example: {type: ‘ADD’}

A new cumulative result (state) is returned.

Reducers specifies how changes in the application state are sent to the Store in response to actions. Remember that actions only describe the fact that something has happened, not how the application updates state.

The reducer is a pure function, that is, the return value does not change if the received parameters are unchanged.

Never do these operations in Reducer:

  • Modify incoming parameters;
  • Perform operations that have side effects, such as API requests and route jumps;
  • Calls to impure functions such asDate.now()Math.random().

The reducer must be kept pure. As long as the parameters passed in are the same, the next calculated state returned must be the same. No special cases, no side effects, no API requests, no variable changes, just do the calculation.

4.2 Creating a Store

Install redux: NPM install redux or Yarn add redux

src/store/index.js

import { createStore } from 'redux';
Define the reducer function
const reducer = (state = 0, action) = > {
  switch (action.type) {
    case 'add':
      return state + 1;
    case 'minus':
      return state - 1;
    default:
      returnstate; }};// Use createStore and pass in the Reducer function to generate a store
const store = createStore(reducer);

export default store;
Copy the code

4.3 use the store

src/pages/ReduxPage.js

import React, { Component } from 'react';
import store from '.. /store';

export default class ReduxPage extends Component {
  componentDidMount() {
    // So far, the page does not automatically update when the state is changed by dispath an action
    // Update the interface in this way for the time being, subscribe to store changes, and then force the page to be updated without using other libraries
    this.unsubscribe = store.subscribe(() = > {
      this.forceUpdate();
    });
  }

  add = () = > {
    store.dispatch({ type: 'ADD' });
  };

  minus = () = > {
    store.dispatch({ type: 'MINUS' });
  };

  render() {
    return (
      <div>
        ReduxPage
        <div>count: {store.getState()}</div>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={()= > this.unsubscribe()}>unsubscribe</button>
      </div>); }}Copy the code

src/App.js

import React from 'react';
import ReduxPage from './page/ReduxPage';

function App() {
  return (
    <div className="App">
      <ReduxPage />
    </div>
  );
}

export default App;
Copy the code

At this point, you can easily use Redux for state management

5. The story

5.1 createStore implementation

Create redux and expose the createStore method

src/redux/index.js

import createStore from './createStore'

export {
  createStore
}
Copy the code

5.1.1 implementation createStore

CreateStore creates a Store instance that contains getState, SUBSCRIBE, Dispatch, and other methods

export default function createStore(reducer) {
  function getSate() {}
  function subscribe() {}
  function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}
Copy the code

5.1.2 Implementing the getState method

GetState simply returns the current equal state

export default function createStore(reducer) {
  // define currentState to save the currentState
  let currentState;

  function getSate() {
    // When getState is called, the current state is returned
    return currentState;
  }
  function subscribe() {}
  function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}
Copy the code

5.1.3 Implement the subscribe method

Subscribe, which saves the subscribed callback functions in the callback array

export default function createStore(reducer) {
  let currentState;
  // Define currentListeners to hold the callback functions of the subscription
  let lisenters = [];

  function getSate() {
    return currentState;
  }
  function subscribe(listener) {
    // Save the callback function in the callback function array
    lisenters.push(listener)
    // Return an unsubscribed function
    return function unsubscribe() {
      const index = lisenters.indexOf(lisenter);
      lisenters.splice(index, 1); }}function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}
Copy the code

5.1.4 Implement the Dispatch method

Dispatch an action (dispatch(action)) is the only way to modify the state.

The Dispatch method calls the Reducer function passed in when the store was created, along with the current state and action.

Finally, the callback collected by the subscribe method is called in a loop.

export default function createStore(reducer) {
  let currentState;
  let lisenters = [];

  function getSate() {
    return currentState;
  }

  function subscribe(listener) {
    lisenters.push(listener);

    return function unsubscribe() {
      const index = lisenters.indexOf(lisenter);
      lisenters.splice(index, 1);
    };
  }

  function dispatch(action) {
    // Call the Reducer function to change the current state
    currentState = reducer(currentState, action)
    // Loop to call the callback function
    for (let i = 0; i < lisenters.length; i++) {
      const lisenter = lisenters[i];
      lisenter()
    }
  }

  return {
    getSate,
    subscribe,
    dispatch,
  }
}
Copy the code

5.1.5 Initializing the State operation

export default function createStore(reducer) {
  let currentState;
  let lisenters = [];

  function getState() {/ * * /}

  function subscribe(listener) {/ * * /}

  function dispatch(action) {/ * * /}

  // Execute the initial dispatch. this ensures that the entire state tree has the initial state value. In this case, the reducer function will take effect only when the initial state is defined.
  dispatch({ type: '@@redux/INIT' });

  return {
    getState,
    subscribe,
    dispatch,
  };
}
Copy the code

Up to here, the basic function of redux has been implemented. In addition, the above methods also need to do some parameter compatibility processing, edge case processing and so on.

Redux (SRC /redux/index.js) : redux (SRC /redux/index.js)

At this point, actions can only support js plain objects. Actions do not support passing a function (asynchronous). For asynchronous operations, passing in an action with a function value also requires middleware.

How to implement asynchronous operations?

So far, using Redux has been limited to using normal objects as actions. If you need to update state asynchronously, such as dynamically fetching data from the back end and then changing state, you can’t do that.

import React, { Component } from 'react';
import store from '.. /store';

export default class ReduxPage extends Component {
  componentDidMount() {
    store.subscribe(() = > {
      this.forceUpdate();
    });
  }

  add = () = > {
    store.dispatch({ type: 'ADD' });
  };

  minus = () = > {
    store.dispatch({ type: 'MINUS' });
  };
	// Pass action as a function to perform an asynchronous operation
  asyncAdd = () = > {
    store.dispatch(() = > {
      setTimeout(() = > {
        store.dispatch({ type: 'ADD' });
      });
    });
  };

  render() {
    return (
      <div>
        <div>count: {store.getState()}</div>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={this.asyncAdd}>asyncAdd</button>
      </div>); }}Copy the code

To implement asynchronous operations, redux middleware needs to be accessed

Use of Redux middleware

Redux is a pure state manager and only supports synchronization by default. Asynchronous tasks such as latency and network requests need middleware support

The store is at the heart of Redux. In addition to the store-related content, Redux also provides a number of other apis for extending and accessing other libraries to achieve even greater functionality. ApplyMiddlewares is used to access middleware applications.

Execution of the middleware occurs between the call to Dispatch and the final reducer to modify the state.

Next, try the simplest of the Thunk and Logger middleware

src/store/index.js

import { createStore, applyMiddleware } from 'redux';
// import { createStore } from '.. /redux';

import thunk from 'redux-thunk';
import logger from 'redux-logger';

const reducer = function (state = 0, action) {
  switch (action.type) {
    case 'ADD':
      return state + 1;
    case 'MINUS':
      return state - 1;
    default:
      returnstate; }};// Use applyMiddleware to access Logger and Thunk middleware
const store = createStore(reducer, applyMiddleware(logger, thunk));

export default store;
Copy the code

The middleware is executed in a certain order. Adjusting the location of the middleware may result in different execution results.

For example, if logger is placed before thunk, you can print out the date of the asynchronous action. If logger is placed after thunk, you cannot print out the date of the asynchronous action, because in the logic of thunk, if action is a function, The function is executed, bypassing subsequent middleware. This is something you might want to know and pay attention to in actual development.

7. Redux middleware implementation

In fact, the middleware enhances and extends the original dispatch, and packages a layer of functions outside the dispatch to perform the functions of the middleware itself. After performing the functions of the middleware itself, the original Dispatch is called.

There may be one or countless pieces of middleware, and to ensure that they are all executed efficiently and in order, there needs to be a good way to string them together.

7.1 Implement the compose function

The compose function implementation strings the middleware together, executes one middleware at a time, and passes the result of the previous middleware execution to the next middleware.

Compose (f1, f2, f3)(… args) => f1(f2(f3(… args)))

The implementation of compose uses the array.prototype.reduce () method.

src/redux/compose.js

export default function compose(. funcs) {
  If funcs is of length 0, return a default function
  if(funcs.length === 0) {
    return arg= > arg
  }
  Return funcs of length 1
  if(funcs.length === 1) {
    return funcs[0]}// Use reduce to concatenate functions
  return funcs.reduce((a,b) = > (. args) = >a(b(... args)))// In order to understand this code, you must first understand the use of Reduce.
  // For example, funcs is [logger, thunk], then logger(thunk(.. args))
  // return funcs.reduce(function(a, b) {
  // return function(... args) {
  // return a(b(... args))
  / /}
  // })
}
Copy the code

7.2 Implementing the applyMiddleware function

The applyMiddleware function is passed into the middleware and returns an enhancement function that enhances the createStore function.

src/redux/applyMiddleware.js

import compose from './compose';

export default function applyMiddleware(. middlewares) {
  return (createStore) = > (reducer) = > {
    let store = createStore(reducer);
    let dispatch = () = > {
      throw new Error(
        'Dispatch is not allowed when the middleware is being built, and will not be applied to other middleware. '
      );
    };
    // Provide middleware to API
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action, ... args) = >dispatch(action, ... args), };// Call the middleware function (build) and return a post call to the data
    const chain = middlewares.map((middleware) = > middleware(middlewareAPI));
    // String up the middleware array using composedispatch = compose(... chain)(store.dispatch);return {
      ...store,
      dispatch,
    };
  };
}
Copy the code

Here, let Dispatch defines a function and assigns dispatch “enhanced” at the end to prevent it from being called when the middleware is being built.

src/redux/createStore.js

Modify the createStore function to add an enhancer parameter that enhances the createStore function

export default function createStore(reducer, enhancer) {
  if (typeofenhancer ! = ='undefined') {
    if (typeofenhancer ! = ='function') {
      throw new Error('Enhancer must be a function.');
    }
    return enhancer(createStore)(reducer);
  }
  / * * /
}
Copy the code

Enhancer is an applyMiddleware when adding store functionality to the middleware using the applyMiddleware function

7.3 Implementing middleware

There are three layers of functions in the middleware,

The first layer of functions, which are middleware builders, are passed getState and Dispatch methods. Initialization of the middleware is also performed here, making these two methods available to the middleware during execution.

The second layer of functions is primarily used to concatenate middleware

The third layer functions are the main logical code implementation of the functions to be extended by the middleware

7.3.1 redux – thunk

// When building the middleware, pass getState and Dispatch to make the middleware use these two methods
const thunk = ({ getState, dispatch }) = > {
  // The second layer is a layer of packet functions generated for compose, where next is the next layer of middleware, and the last next is the original dispatch
  return (next) = > {
    // Main logic code of middleware
    return (action) = > {
      if (typeof action === 'function') {
        return action(dispatch, getState);
      }
      return next(action);
    };
  };
};
Copy the code

8. react-redux

With redux alone, you can only subscribe to data changes, manually update the interface, and get the latest data through getState every time.

It’s a bit cumbersome to do that.

To better combine React with Redux, you need a library like React-Redux.

8.1 the react – redux API

React-redux provides two apis: Provider and Connect

API

  • <Provider store>
  • connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
  1. ProviderMake the component hierarchyconnect()Method can get the Redux store. Normally, your root component should be nested in<Provider>Can be used inconnect()Methods.
  2. connectAs the name implies, this connects the Redux Store to the component. It is a high-order component (HOC) that passes in a component and returns a new component that extends the original component so that the original component can access the data in SOTRE and changes the method of data. Wiring does not change the original component class. butreturnA new component class that has been connected to the Redux Store.

8.2 the react – use redux

Change the example above to use React-redux

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from './react-redux';
import store from './store'

// Use the Provider to provide stores for future components, so that the connect() method in the component hierarchy can obtain the Redux Store
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>.document.getElementById('root'));Copy the code

src/page/ReactReduxPage.js

import React, { Component } from 'react';
import { connect } from '.. /react-redux';

class ReactReduxPage extends Component {
  add = () = > {
    this.props.dispatch({ type: 'ADD' });
  };

  minus = () = > {
    this.props.dispatch({ type: 'MINUS' });
  };

  asyncAdd = () = > {
    this.props.dispatch((dispatch) = > {
      setTimeout(() = > {
        dispatch({ type: 'ADD' });
      }, 1000);
    });
  };

  render() {
    return (
      <div>
        <div>count: {this.props.count}</div>{/ *<button onClick={this.add}>add</button>* /}<button onClick={this.props.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={this.asyncAdd}>asyncAdd</button>
      </div>); }}// mapStateToProps is used to pass the redux state to the component
// mapDispatchToProps is used to encapsulate dispatchToprops and action methods, and then pass them to the component. It is easy for the component to change state without having to write a lot of dispatchtoprops.
const mapStateToProps = (state) = > ({ count: state });
// mapDispatchToProps specifies how to use an object
const mapDispatchToProps = {
  add: () = > ({ type: 'ADD'})};// mapDispatchToProps () functions to use
// const mapDispatchToProps = (dispatch) => {
// return {
// dispatch,
// add: () => dispatch({ type: 'ADD' }),
/ /};
// };

// Connect the component to the store using the connect method
// The incoming mapStateToProps and mapDispatchToProps define the state and dispatch to be passed to the component
export default connect(mapStateToProps, mapDispatchToProps)(ReactReduxPage);
Copy the code

8.3 React API: Context

Here the Context is used to pass the state in redux to the components, making it easy for the components to use the data in the redux state.

In a typical React application, data is passed from top to bottom (by parent and child) via the props attribute, but this is extremely cumbersome for certain types of attributes (e.g., locale preferences, UI themes) that are required by many components in the application. Context provides a way to share such values between components without having to explicitly pass props through the component tree.

Context is used less in daily development and more in third-party libraries. This feature is used in react-Redux.

The Context of use

// Context allows us to pass values deep into the component tree without explicitly passing them through each component.
// Create a context for the current theme (" light "is the default).
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the following component tree.
    // This value can be read by any component, no matter how deep.
    // In this example, we pass "dark" as the current value.
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>); }}// The middle component no longer has to specify to pass theme down.
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // contextType reads the current theme context.
  // React will look up to the nearest theme Provider and use its value.
  // In this example, the current theme value is "dark".
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />; }}function Button({ theme }) {
  return <button style={{ backgroundColor: theme}} >Test Button</button>;
}

// You can also use Consumer to get the context
// function ThemedButton () {
// return (
// 
      
// {value => }
// 
/ /)
// }

// or use hooks, useContext()
Copy the code

React-redux API: bindActionCreators

BindActionCreators, apply with connect, mapDispatchToProps is used to bind the value (ActionCreator) corresponding to each key in the object with a dispatch call. Then pass the props to the component to make it easier for the component to change the state of the redux and reduce the number of dispatches (XXX) in the component. We’ll talk about that in Action Creator. Use: bindActionCreators(actionCreators, dispatch)

BindActionCreators, apply a value with a different ActionCreator, and turn it into a key with the same name. Each Action Creator is also wrapped with Dispatches so that they can be called directly.

The effect is something like this:

{
  add: () = > ({ type: 'ADD'})};/ / into
{
	add: () = > dispatch({ type: 'ADD'})}Copy the code

Parameters:

  1. actionCreators (Function or Object): An Action Creator, or a value is an Action Creator object.
  2. dispatch (Function) : aStoreInstance provideddispatchFunction.

8.4.1 Action Creator

Action Creator is the function used to generate Action

Such as:

function ADD_TODO(text) {
	return { type: 'ADD', text}
}
dispatch(ADD_TODO('To-do list 1')) // Dispatch ({type: 'ADD', text: '1'})
Copy the code

In the example above, ADD_TODO is used to generate Action {type: ‘ADD’, text}. ADD_TODO is an Action Creator function

An Action is a payload of information, and Action Creator is a factory for creating actions.

8.4.1 bindActionCreators implementation

{add: () => ({type: ‘add ‘})} equals add = () => dispatch({type:’ add ‘})

// bindActionCreator wraps the ActionCreator, plus the dispatch call
function bindActionCreator(actionCreator, dispatch) {
  return (. args) = >dispatch(actionCreator(... args)); }export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreators = {};
  for (let key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); }}return boundActionCreators;
}
Copy the code

8.4 the react – redux implementation

src/react-redux/index.js

import Provider from './Provider';
import connect from './connect';
// Expose the Provider, connect API
export { Provider, connect };

Copy the code

Create a Context for data communication

src/react-redux/Context.js

import React from 'react';

export const ReactReduxContext = React.createContext(null);

export default ReactReduxContext;
Copy the code

8.5 Provider Implementation

import React, { Component } from 'react';
import { ReactReduxContext } from './Context';

export default class Provider extends Component {
  render() {
    // Use context. Provider to store components
    return (
      <ReactReduxContext.Provider value={this.props.store}>
        {this.props.children}
      </ReactReduxContext.Provider>); }}Copy the code

8.6 Connect implementation

Connect takes the mapStateToProps and mapDispatchToProps arguments and returns a high-level component.

The connection uses the component of the Store and passes the value in the Store to the component

import React, { useLayoutEffect, useReducer, useContext } from 'react';
// import { bindActionCreators } from 'redux';
import { bindActionCreators } from '.. /redux';
import { ReactReduxContext } from './Context';

const connect = (mapStateToProps = (state) => state, mapDispatchToProps) = > (
  WrappedComponent
) = > (props) = > {
  const store = useContext(ReactReduxContext);
  const { getState, dispatch, subscribe } = store;

  // mapStateToProps is the first parameter passed when using connect,
  MapStateToProps = props; // getState = props
  // mapStateToProps Returns the props to be passed to the component
  const stateProps = mapStateToProps(getState());

  // Connect's second argument mapDispatchToProps can be an object or function
  let dispatchProps;
  if (typeof mapDispatchToProps === 'object') {
    // If mapDispatchToProps is an object, use bindActionCreators to package the object into a function object that can be called directly
    dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
  } else if (typeof mapDispatchToProps === 'function') {
    // If mapDispatchToProps is a function, call the function and pass dispatch
    dispatchProps = mapDispatchToProps(dispatch);
  } else {
    // The default is dispatch
    dispatchProps = { dispatch };
  }
	
  function storeStateUpdatesReducer(state, action) {
    return state + 1;
  }
  // Use useReducer to force updates to the page
  // useReducer returns an array containing two items [state, Dispatch]. When dispatch returns a new state, the component rerenders
  const [, forceComponentUpdateDispatch] = useReducer(
    storeStateUpdatesReducer,
    0
  );

  useLayoutEffect(() = > {
    // Subscribe to update the data in the store to force the refresh page
    const ubsubscribe = subscribe(() = > {
      forceComponentUpdateDispatch({ type: 'STORE_UPDATED' });
    });
    return () = > {
      // Unsubscribe the component
      ubsubscribe && ubsubscribe();
    };
  }, [store]);

  // Return the component that needs to be "wired" and pass the data that the component needs
  return <WrappedComponent {. props} {. stateProps} {. dispatchProps} / >;
};

export default connect;
Copy the code

9. redux devtools

9.1 access to the story

import { createStore, applyMiddleware } from 'redux';
/ / introduce composeWithDevTools
import { composeWithDevTools } from 'redux-devtools-extension';

import thunk from 'redux-thunk';
import logger from 'redux-logger';

const reducer = function (state = 0, action) {
  switch (action.type) {
    case 'ADD':
      return state + 1;
    case 'MINUS':
      return state - 1;
    default:
      returnstate; }};const store = createStore(
  reducer,
  composeWithDevTools(applyMiddleware(logger, thunk))
);
export default store;

Copy the code

Redux Devtools is a Chrome extension

  1. Open the Chrome debugging tool and select Redux to go to the debugging screen of Redux DevTools.

  2. Select the functions to use: Log Monitor, Inspector, and Chart. The default is Inspector.

  3. A list of actions executed during the recorded redux execution. Click each action to enter the detailed status of the action

  4. Action You can view details about the current Action

  5. State Displays details about the current State data

  6. Diff Displays the state changes after the current action is executed

  7. Trace Traces the code where the current action is executed

  8. Test Test template

  9. Start recording/Pause Recording Starts/stops recording. You can specify when recording starts and when recording stops.

  10. Lock changes Locks the current recording state. Action does not change the current recording state.

  11. The Dispatcher dispatches actions. You can edit actions in the Dispatcher box.

  12. Slider is used to automatically play the entire recording process.

  13. Import/ Export Export the current recorded JSON file, which is used for Import. After Import, the file will be restored to the saved state.

  14. Settings is the redux DevTools configuration menu.

  15. Switch to Log Monitor to view the logs of the entire process

  16. Switch the function panel to Chart to view the status of the entire store

Read more:

Liverpoolfc.tv: redux.js.org/

Making: github.com/reduxjs/red…

Chinese documentation: www.redux.org.cn/

The React Context:zh-hans.reactjs.org/docs/contex…

Redux: You Might Not Need redux