@Basic concepts of medux

Time:2020-9-24

Welcome to the @medux tour. It is recommended that you read the following 4 articles in turn, which will take you about 30 minutes.

  • Why do you need @ medux
  • @Basic concepts of medux
  • @Medux routing
  • @Medux data stream

Chapter 2: a brief introduction to the basic concepts of medux

–GitHub address—

Eight new concepts and terms

Suppose you knowReduxOr somethingFluxFramework, then you should know what store, state, reducer, action and dispatch mean. Yes, they still work in @ medux, but the concept of action has changed a little bit. It has more features of event events.

Effect

We know that in Redux, changing the state must trigger the reducer through the dispatch action. The effect is relative to the reducer, and it must be triggered through the dispatch action

  • It is an impure function that can contain side effects, can have no return, or it can be asynchronous
  • It can’t directly change the state, and must dispatch action again to trigger the reducer

ActionHandler

We can simply think of it as follows: store.dispatch (action), which can trigger the execution of reducer and effect. It seems that action can be regarded as an event. Reducer and effect can be regarded as listeners of the event, so they are collectively referred to as:ActionHandler。

Module

We usually useHigh cohesion and low couplingThe principle of module division is a relatively independent collection of business functions. It usually contains a model (used to process business logic) and a set of views (used to show data and interaction). It is important to note that: do not use UI vision as the division principle.

Model

As mentioned above, the module includesA model (maintenance data)andA set of viewsThe model mainly includes two functions:

  • Definition of modulestate
  • Definition of actionhandler

Data flow flows from model to view in one direction, so model is independent and independent of view. In theory, even without view, the whole program can still be driven by data.

ModuleState、RootState

The system is divided into several relatively independent modules, which are not only reflected in the folder directory, but also in the state data structure. Each module is responsible for maintaining and managing a child node under the state, which we callModuleStateAnd the whole state is what we used to call itRootState

  • Each modulestate is a primary child node of the store, with the module named key
  • Each module can only modify its own modulestate, but can read other modulestates
  • Each module modifies its own modulestate, which must be triggered by the dispatch action
  • Each module can monitor the actions issued by other modules to modify its own module state

View、Component

View is also a component in essence. There are logical differences between them

  • View is used to show business and component is used to show interaction
  • View must belong to a module, and component can belong to a specific module or all modules
  • View usually subscribes to the store and gets data from stores, while component can only be delivered through props

Typical engineering structure

src
├ - assets // storing public static resources
├ - entity // stores business entity type definitions
├ - common // store public code
├ - components // store common UI components
├── modules
A module named app
\\\\\\\\\
Component // stores the private UI components of the module
│       │     ├── views
│       │     │     ├── TopNav
│       │     │     ├── BottomNav
│       │     │     └── ...
│       │     ├──  model.ts  //Define the module model
│       │     └──  index.ts  //Export this module
颴∧ƆƆƆƆƆƆ颴Ɔ颴颴ƆƆ∧ƆƆ颴ƆƆƆ
│       │     └── ...
│       └──  index.ts  //Module configuration and management
└── index.ts  Start the entrance

Definition of a model

//Define the modulestate type of this module
export interface State extends BaseModuleState {
  listSearch: {username: string; page: number; pageSize: number};
  listItems: {uid: string; username: string; age: number}[];
  listSummary: {page: number; pageSize: number; total: number};
  loading: {
    searchLoading: LoadingState;
  };
}

//Define the initial value of the modulestate of this module
export const initState: State = {
  listSearch: {username: null, page: 1, pageSize: 20},
  listItems: null,
  listSummary: null,
  loading: {
    searchLoading: LoadingState.Stop,
  },
};

//All actionhandlers defined in this module
class ModuleHandlers extends BaseModuleHandlers<State, RootState> {
  @reducer
  public putSearchList({listItems, listSummary}): State {
    return {...this.state, listItems, listSummary};
  }
  @effect('searchLoading')
  public async searchList(options: {username?: string; page?: number; pageSize?: number} = {}) {
    const listSearch = {...this.state.listSearch, ...options};
    const {listItems, listSummary} = await api.searchList(listSearch);
    this.dispatch(this.action.putSearchList({listItems, listSummary}));
  }
  //You can listen to the actions issued by other modules, and then change your own modulestate
  @effect(null)
  protected async ['medux.RouteChange']() {
    if (this.rootState.route.location.pathname === '/list') {
      await this.dispatch(this.action.searchList());
    }
  }
}

Static and dynamic module loading mechanism

The loading strategy of modules is usually focused on modules/ index.ts Medium configuration:

import * as app from 'modules/app';

//Define the loading scheme of the module, and it can be synchronous or asynchronous
export const moduleGetter = {
  app: () => {
    //Synchronous loading with import
    return app;
  },
  photos: () => {
    //Use import() to load asynchronously
    return import(/* webpackChunkName: "photos" */ 'modules/photos');
  },
};

Several special built-in actions

  • medux.RouteChange: this action will be triggered when the route changes
  • medux.Error: this action will be automatically dispatched when an error is caught
  • moduleName.Init: this action is triggered when the module is first loaded
  • moduleName.RouteParams: this action will be triggered when the module routing parameters change
  • moduleName.Loading: this action is triggered when the load progress is tracked

About error handling

When an error occurs during execution, the framework will automatically dispatch a type ofmedux.ErrorYou can listen to this action to handle errors. For example:

@effect(null)
  protected async [ActionTypes.Error](error: CustomError) {
    if (error.code === '401') {
      this.dispatch(this.actions.putShowLoginPop(true));
    } else if (error.code === '404') {
      this.dispatch(this.actions.putShowNotFoundPop(true));
    } else {
      error.message && Toast.fail(error.message);
      //If you continue to throw up, the operation will be interrupted
      throw error;
    }
  }

Route stateization

  • State = Combine(Route)
  • UI = Render(State)

Medux regards routing as another store similar to Redux, which records the real-time status of applications just like redux. However, Redux is recorded in memory and automatically maintained by the program, while route is recorded in the browser address bar and manually entered and maintained by the user. For example:

To display and close the comment block, you can trigger it in two ways:

  • ‘/ article / 10’ = > ‘/ article / 10 / showcomments’, route changes can cause the display and closing of comment blocks
  • {showcomments: false} = > showcomments: true}, the state change can achieve the same effect

Is routing control or state control used? We hope that there is no intentional distinction in the component, so that it is not necessary to move to the component itself when modifying the scheme later.

You think of routing as another storeHowever, the routestore can be modified directly by the user in the address bar, which is essentially the same as the user’s mouse click interactive modification. So be prepared to extract part of the data from reduxstore and put it into routestore, and then let the user modify it arbitrarily through the URL

The ultimate purpose of routing is to change the UI. Therefore, no matter what routing scheme is, the following general information can always be resolved:

  • What views will the current route display
  • And the parameters needed to display these views

Medux abstracts these general information into states.At this point, you can forget the routing, everything is state, everything follows UI = render (state)。 So the routing component with side effects turned into a common component:

//The routing component was originally required
<Switch>
  <Route exact path="/admin/home" component="{AdminHome}" />
  <Route exact path="/admin/role/:listView" component="{AdminRole}" />
  <Route path="/admin/member/:listView" component="{AdminMember}" />
</Switch>

//Now it's a normal component
<Switch>
  {routeViews.adminHome?.Main && <AdminHome />}
  {routeViews.adminRole?.List && <AdminRole />}
  {routeViews.adminMember?.List && <AdminMember />}
</Switch>

How to extract general information and transform it into state? There are many schemes, and I have implemented one:

  • @medux/route-plan-a

You can also implement more plan-b, plan-c

CoreAPI

View coreapi documentation

Demo

  • Medux react admin: Based on@medux/react-web-routerAnd the latestANTD 4.xIn addition to demonstrating how medux is used, the general background management system developed has created many unique concepts
  • Medux react SSR: fork transformed medux react admin toServer isomorphic rendering, you can see how to put a single page(Single page application)Fast conversion to multi page applications that support SEO.

Read on to the next one

@Medux routing