{"id":13680978,"url":"https://github.com/wooline/react-coat","last_synced_at":"2025-04-09T06:13:29.803Z","repository":{"id":97678234,"uuid":"122799738","full_name":"wooline/react-coat","owner":"wooline","description":"Structured React + Redux with Typescript and support for isomorphic rendering beautifully(SSR)","archived":false,"fork":false,"pushed_at":"2020-04-26T17:37:50.000Z","size":1334,"stargazers_count":286,"open_issues_count":2,"forks_count":11,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-02T02:19:15.588Z","etag":null,"topics":["mobx","react","redux","server-side-rendering","ssr","state-management","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wooline.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-02-25T02:49:44.000Z","updated_at":"2025-03-31T13:23:23.000Z","dependencies_parsed_at":"2023-06-07T21:45:42.360Z","dependency_job_id":null,"html_url":"https://github.com/wooline/react-coat","commit_stats":{"total_commits":123,"total_committers":3,"mean_commits":41.0,"dds":"0.19512195121951215","last_synced_commit":"9470cdd6afb9e8251466872833ab0521625f1502"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooline%2Freact-coat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooline%2Freact-coat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooline%2Freact-coat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooline%2Freact-coat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wooline","download_url":"https://codeload.github.com/wooline/react-coat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987285,"owners_count":21028895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mobx","react","redux","server-side-rendering","ssr","state-management","typescript"],"created_at":"2024-08-02T13:01:24.522Z","updated_at":"2025-04-09T06:13:29.778Z","avatar_url":"https://github.com/wooline.png","language":"JavaScript","funding_links":[],"categories":["React"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/wooline/react-coat.svg?branch=master)](https://travis-ci.org/wooline/react-coat)\n[![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/)\n[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)\n\n### I'm not going to maintain this framework anymore, because there's a better alternative, That is another project for me and can be seen as the next version of the this framework:\n\n# [Welcome to Medux](https://github.com/wooline/medux)\n\n\nThis package is already outdated! Welcome to [medux](https://github.com/wooline/medux)\n\n**English** | [简体中文](./README_zh-CN.md)\n\n- The framework is React state and data flow management. It does not improve and encapsulate React itself, nor does it violate the style and trend of React FP.\n- The framework uses Class to organize the Model, supports inheritance, but does not force the use of inheritance, sometimes inheritance will increase the complexity of the project.\n- Criticism and correction are welcome. Feedback if there are any mistakes or Bugs. Don't forget to **give a Star** if you think it's good.\\_\u003c\n- Why? Article Recommendation：[https://zhuanlan.zhihu.com/p/58583636](https://zhuanlan.zhihu.com/p/58583636)\n\nThe opening, freedom and prosperity of react ecosphere also lead to tedious development and configuration and confused choice.Reaction-coat abandons some flexibility, replaces some configurations with conventions, solidifies some best practices, and provides developers with a more concise sugar coat.\n\nAre you still honestly maintaining the store according to the native Redux tutorial?Try react-coat, which is so simple that you can do it almost without learning.\n\nFor example:\n\n```JS\n//  Only one class, action、reducer、effect、loading\nclass ModuleHandlers extends BaseModuleHandlers {\n  @reducer\n  protected putCurUser(curUser: CurUser): State {\n    return {...this.state, curUser};\n  }\n  @reducer\n  public putShowLoginPop(showLoginPop: boolean): State {\n    return {...this.state, showLoginPop};\n  }\n  @effect(\"login\") // use loading state\n  public async login(payload: {username: string; password: string}) {\n    const loginResult = await sessionService.api.login(payload);\n    if (!loginResult.error) {\n      // this.updateState() is a shortcut to this.dispatch(this.actions.updateState())\n      this.updateState({curUser: loginResult.data});\n      Toast.success(\"welcome！\");\n    } else {\n      Toast.fail(loginResult.error.message);\n    }\n  }\n  // uncatched error will dispatch @@framework/ERROR action\n  // subscribe it and reporting to the server\n  @effect(null) // set null that means loading state are not needed\n  protected async [\"@@framework/ERROR\"](error: CustomError) {\n    if (error.code === \"401\") {\n      // dispatch action: putShowLoginPop\n      this.dispatch(this.actions.putShowLoginPop(true));\n    } else if (error.code === \"301\" || error.code === \"302\") {\n      // dispatch action: router change\n      this.dispatch(this.routerActions.replace(error.detail));\n    } else {\n      Toast.fail(error.message);\n      await settingsService.api.reportError(error);\n    }\n  }\n  // subscribe itself's INIT Action and to do any async request\n  @effect()\n  protected async [\"app/INIT\"]() {\n    const [projectConfig, curUser] = await Promise.all([\n      settingsService.api.getSettings(),\n      sessionService.api.getCurUser()\n    ]);\n    // this.updateState() is a shortcut to this.dispatch(this.actions.updateState())\n    this.updateState({\n      projectConfig,\n      curUser,\n    });\n  }\n}\n```\n\nStatic checking and intelligent prompts with Typescript：\n\n![TS Types](https://github.com/wooline/react-coat/blob/master/docs/imgs/4.png)\n\n---\n\n\u003c!-- TOC --\u003e\n\n- [4.0 Released](#40-released)\n- [Feature](#feature)\n- [why not dvaJS](#why-not-dvajs)\n- [Install](#install)\n- [Compatibility](#compatibility)\n- [List of API](#list-of-api)\n- [Quick Start and Demo](#quick-start-and-demo)\n- [Basic concepts and nouns](#basic-concepts-and-nouns)\n  - [Store、Reducer、Action、State、Dispatch](#storereduceractionstatedispatch)\n  - [Effect](#effect)\n  - [ActionHandler](#actionhandler)\n  - [Module](#module)\n  - [ModuleState、RootState](#modulestaterootstate)\n  - [Model](#model)\n  - [View、Component](#viewcomponent)\n- [Routing and Dynamic Loading](#routing-and-dynamic-loading)\n- [Several special actions](#several-special-actions)\n- [Roadmap](#roadmap)\n\n\u003c!-- /TOC --\u003e\n\n## 4.0 Released\n\n- Remove redux-saga and use es6 async and await to organize and manage effects\n- Support SPA (single page application) and SSR (server side rendering), complete support client and server isomorphism\n\n## Feature\n\n- Integrated react, redux, react-router, history and other related frameworks\n- Sugarcoat for the above frame only, does not change its basic concept, does not have strong invasion and destructiveness.\n- Structured front-end engineering, business modularization, support on-demand loading\n- Support SPA (single page application) and SSR (server side rendering)\n- Using Typescript, better static checking and intelligent prompts\n- Open source micro framework, less than 1000 lines of source code, almost without learning to start\n\n## why not dvaJS\n\n\u003e The framework is similar to `dvajs` in concept, and the main differences are as follows:：\n\n- Introduce the ActionHandler Observer Model to collaborate between more elegant processing modules.\n- Remove redux-saga and replace it with async and await to simplify the code and support `TS Types` more comprehensively.\n- Using Typescript organization and development, more comprehensive type safety.\n- Module division by business function, Page-free concepts.\n- Routing componentization and not configuration, simpler organizational structure.\n- Support SPA (single page application) and SSR (server rendering) isomorphic.\n- [**More differences with dvaJS**](https://github.com/wooline/react-coat/blob/master/docs/vs-dva.md)\n\n## Install\n\n    $ npm install react-coat\n\npeerDependencies\n\n```\n  \"peerDependencies\": {\n    \"@types/node\": \"^9.0.0 || ^10.0.0  || ^11.0.0\",\n    \"@types/history\": \"^4.0.0\",\n    \"@types/react\": \"^16.0.0\",\n    \"@types/react-dom\": \"^16.0.0\",\n    \"@types/react-redux\": \"^5.0.0 || ^6.0.0 || ^7.0.0\",\n    \"@types/react-router-dom\": \"^4.0.0\",\n    \"connected-react-router\": \"^5.0.0 || ^6.0.0\",\n    \"history\": \"^4.0.0\",\n    \"react\": \"^16.3.0\",\n    \"react-dom\": \"^16.3.0\",\n    \"react-redux\": \"^5.0.0 || ^6.0.0\",\n    \"react-router-dom\": \"^4.0.0\",\n    \"redux\": \"^3.0.0 || ^4.0.0\"\n  }\n\n```\n\nIf you want to save your mind and have no special requirements for the dependent versions, you can install the [**react-coat-pkg**](https://github.com/wooline/react-coat-pkg) of \"all in 1\", which will automatically contain the above libraries and the versions pass without conflict after test.\n\n    $ npm install react-coat-pkg\n\n## Compatibility\n\nMainstream browser、\u003e=IE9 (with es6 polyfill，recommend @babel/polyfill)\n\n## List of API\n\n[Click to view](./API.md)\n\n```\n\nBaseModuleHandlers, BaseModuleState, buildApp, delayPromise, effect, ERROR, errorAction, exportModel, exportModule, exportView, GetModule, INIT, LoadingState, loadModel, loadView, LOCATION_CHANGE, logger, ModelStore, Module, ModuleGetter, reducer, renderApp, RootState, RouterParser, setLoading, setLoadingDepthTime\n\n```\n\n## Quick Start and Demo\n\nThe framework is simple to use.\n\n- 8 new concepts：\n\n  \u003e Effect、ActionHandler、Module、ModuleState、RootState、Model、View、Component\n\n- 4 steps to create:\n\n  \u003e exportModel(), exportView(), exportModule(), createApp()\n\n- 3 demos, Step by Step:\n\n  \u003e [Single Page Helloworld](https://github.com/wooline/react-coat-helloworld)\n\n  \u003e [Single Page Optimization](https://github.com/wooline/react-coat-spa-demo)\n\n  \u003e [SPA+SSR (Server-Side Rendering)](https://github.com/wooline/react-coat-ssr-demo)\n\n## Basic concepts and nouns\n\nPremise: Suppose you are familiar with React and Redux and have some development experience.\n\n### Store、Reducer、Action、State、Dispatch\n\nThe above concepts are basically the same as Redux. The framework is non-intrusive and follows the concepts and principles of react and redux:\n\n- Using one-way data flow between M and V.\n- Keep a single Store for the whole station.\n- Store is Immutability immutable data.\n- To change the store state, you need reducer.\n- Calling Reducer must pass explicit dispatch action.\n- Reducer must be pure function\n- Behaviors with side effects, all in the Effect function\n- Each reducer can only modify a node under Store, but it can read all nodes.\n- Componentized routing without centralized configuration\n\n### Effect\n\nWe know that in Redux, changing the state must trigger the reducer through dispatch action and return a new State in the reducer. The reducer is a pure function with no side effects. As long as the input parameters are the same, the return results are the same and are executed synchronously.And effect is relative to reducer. Like reducer, it must also be triggered by dispatch action. The difference is:\n\n- It is a non-pure function that can contain side effects. It can be either non-return or asynchronous.\n- It can't change state directly. To change state, it must dispatch action again to trigger reducer.\n\n### ActionHandler\n\nWe can simply think that:In Redux, store.dispatch(action) can trigger a registered reducer, which seems to be an observer mode. Extending to the above concept of effect, effect is also an observer.An action is dispatched, which may trigger multiple observers to be executed. They may be reducer or effect. So reducer and effect are collectively called: **ActionHandler**\n\n- If a group of actionHandlers are subscribe to an action at the same time, what is their execution order?\n\n  Answer: When an action is dispatched, all reducers are executed first, and they are executed synchronously in turn. After all reducer have been executed, all effect execution will begin.\n\n- I want to wait for this set of actionHandlers to complete the execution, then the next step, but the effect is asynchronous, how do I know that all the effects have been processed?\n\n  Answer: The framework improves the store.dispatch method. If has any effect subscribe to this action, it will return a Promise, so you can use await store.dispatch({type: search\"}) to wait for all effect processing to complete.\n\n### Module\n\nWhen we receive a complex front-end project, we first need to simplify the complexity and disassemble the functions.It is usually divided into Modules according to the principles of high cohesion and low coupling. A module is a collection of relatively independent business functions. It usually includes a Model ( for processing business logic ) and a group of View ( for render data and interaction ). It should be noted that:\n\n- SPA applications have no boundaries of Page. Don't divide modules by Page concepts.\n- A Module may contain a set of Views. Don't divide modules by the concept of View\n\nModule is a logical division, but we are used to using folder directories to organize and reflect, for example:\n\n```\nsrc\n├── modules\n│       ├── user\n│       │     ├── userOverview(Module)\n│       │     ├── userTransaction(Module)\n│       │     └── blacklist(Module)\n│       ├── agent\n│       │     ├── agentOverview(Module)\n│       │     ├── agentBonus(Module)\n│       │     └── agentSale(Module)\n│       └── app(Module)\n```\n\nAs can be seen from the above, the project includes seven modules: app、userOverview、userTransaction、blacklist、agentOverview、agentBonus、agentSale, Although there are subdirectories user and angent under the modules directory, they are only classified and do not belong to modules. We agree that:\n\n- Each Module is a separate folder\n- Module itself has only one level, but it can be categorized in a multilevel directory\n- Each Module folder name is the Module name, because all Modules are level-level, so we need to ensure that the Module name does not repeat, in practice, we can use the Enum type of Typescript to ensure that, you can also put all Modules in the first directory.\n- Each module maintains a certain degree of independence and can be loaded synchronously, asynchronously, on-demand and dynamically.\n\n### ModuleState、RootState\n\nThe system is divided into several relatively independent and level Modules, not only in the folder directory, but also in Store State. Each Module is responsible for maintaining and managing a node under the Store, which we call **ModuleState**, while the entire Store is customarily called **RootState**.\n\nFor example:A Store data structure:\n\n```JS\n\n{\nrouter:{...},// StoreReducer\napp:{...}, // ModuleState\nuserOverview:{...}, // ModuleState\nuserTransaction:{...}, // ModuleState\nblacklist:{...}, // ModuleState\nagentOverview:{...}, // ModuleState\nagentBonus:{...}, // ModuleState\nagentSale:{...} // ModuleState\n}\n```\n\n- Each Module manages and maintains a node under the Store, which we call ModuleState.\n- Each ModuleState is the root node of Store and is named Key by Module.\n- Each Module can only update its own ModuleState, but it can read other ModuleState.\n- Each Module update its own Module State and must be triggered by dispatch action.\n- Each Module can be the identity of the observer and subscribe for actions emanating from other Modules to cooperate with modifying its own Module State.\n\nYou may notice that the first of the Store's sub-nodes above is `router`, which is not a ModuleState, but a node generated by a third-party Reducer.We know that Redux allows multiple Reducers to co-maintain Stroe and provides a combineReducers method for merging. Because the key name of ModuleState is Module name, so:`Module names naturally cannot be renamed with other third-party Reducers`.\n\n### Model\n\nWithin Module, we can further divide it into `a model` (maintenance data) and `a set of views` (render data). Here, the model actually refers to the view model, which mainly contains two functions:\n\n- ModuleStated Definition\n- Maintenance of ModuleState. ActionHandler has been introduced before. There is actually coding for ActionHandler.\n\n\u003e Data flow flows from Model into View in one direction, so Model is independent and independent of View.So in theory, even without View, the program can still be driven from the command line.\n\nWe agree that:\n\n- Focus on coding Model in a file named **model.js**, and put this file under the root directory of this module.\n- Coding all **ActionHandlers** in a class called ModuleHandlers, and each reducer and effect corresponds to a method in that class.\n\nFor example, Model in the userOverview module:\n\n```\nsrc\n├── modules\n│       ├── user\n│       │     ├── userOverview(Module)\n│       │     │         ├──views\n│       │     │         └──model.ts\n│       │     │\n```\n\nsrc/modules/user/userOverview/model.ts\n\n```JS\n// Define ModuleState types\nexport interface State extends BaseModuleState {\n  listSearch: {username:string; page:number; pageSize:number};\n  listItems: {uid:string; username:string; age:number}[];\n  listSummary: {page:number; pageSize:number; total:number};\n  loading: {\n    searchLoading: LoadingState;\n  };\n}\n\n// Coding Module's ActionHandler\nclass ModuleHandlers extends BaseModuleHandlers\u003cState, RootState, ModuleNames\u003e {\n  constructor() {\n    // Define ModuleState's initial value\n    const initState: State = {\n      listSearch: {username:null, page:1, pageSize:20},\n      listItems: null,\n      listSummary: null,\n      loading: {\n        searchLoading: LoadingState.Stop,\n      },\n    };\n    super(initState);\n  }\n\n  // Define a reducer\n  @reducer\n  public putSearchList({listItems, listSummary}): State {\n    return {...this.state, listItems, listSummary};\n  }\n\n\n  // Define a effect that request data with ajax\n  // And then dispatch a action that tigger putSearchList reducer\n  // this.dispatch is store.dispatch's feference\n  // searchLoading indicates to inject the execution state of this effect into State.loading.searchLoading\n  @effect(\"searchLoading\")\n  public async searchList(options: {username?:string; page?:number; pageSize?:number} = {}) {\n    // this.state is own ModuleState\n    const listSearch = {...this.state.listSearch, ...options};\n    const {listItems, listSummary} = await api.searchList(listSearch);\n    this.dispatch(this.action.putSearchList({listItems, listSummary}));\n  }\n\n  // Define a effect that subscribed another module's action and then update its own ModuleState\n  // Use protected permission because there is no need to actively call\n  // @effect(null) indicates that there is no need to track the execution state\n  @effect(null)\n  protected async [\"@@router/LOCATION_CHANGE]() {\n      // this.rootState is the entire store state\n      if(this.rootState.router.location.pathname === \"/list\"){\n          await this.dispatch(this.action.searchList());\n      }\n  }\n}\n```\n\nIn particular, the last ActionHandler of the above code:\n\n```JS\nprotected async [\"@@router/LOCATION_CHANGE](){\n    if(this.rootState.router.location.pathname === \"/list\"){\n        await this.dispatch(this.action.searchList());\n    }\n}\n```\n\nTwo points have been emphasized before:\n\n- Module can subscribe to actions from other modules and cooperate to update its own ModuleState.\n- Module can only update its own ModuleState node, but it can read the entire Store.\n\nAlso note the statement：await this.dispatch(this.action.searchList())：\n\n- It's understandable that dispatch dispatches an action called searchList, but why can we still have awiat before? Is dispatch action asynchronous?\n\n  Answer: The dispatch dispatch action itself is synchronous. We talked about the concept of ActionHandler before. When an action is dispatch, there may be a group of reducers or effects subscribe to it simultaneously. Reducers are synchronous, but effects may be asynchronous. If you want to wait for all the concurrent subscriber to be completed, you can use await here. Otherwise, you can not use await.\n\n### View、Component\n\nWithin the Module, we can further divide it into a model ( maintenance data ) and a group of view ( render data ).So there may be more than one view in a Module, and we are used to creating a folder named views under the Module root directory:\n\nFor example, views in the userOverview module:\n\n```\nsrc\n├── modules\n│       ├── user\n│       │     ├── userOverview(Module)\n│       │     │         ├──views\n│       │     │         │     ├──imgs\n│       │     │         │     ├──List\n│       │     │         │     │     ├──index.css\n│       │     │         │     │     └──index.ts\n│       │     │         │     ├──Main\n│       │     │         │     │    ├──index.css\n│       │     │         │     │    └──index.ts\n│       │     │         │     └──index.ts\n│       │     │         │\n│       │     │         │\n│       │     │         └──model.ts\n│       │     │\n```\n\n- Each view is actually a React Component, so start with uppercase letters.\n- For accessory resources such as CSS and img, if they belong to a view private, follow the view together, and if multiple views are public, put them in the public directory.\n- Views can be nested, including views that can be nested in other modules. If you need to use them for other modules, you must use `exportView()` in `views/index.ts` to export.\n- ActionHandler in Model is triggered by dispatch action in view. In addition to dispatch action of this module, it can also dispatch action of other modules.\n\nFor example: LoginForm：\n\n```JS\ninterface Props extends DispatchProp {\n  logining: boolean;\n}\n\nclass Component extends React.PureComponent\u003cProps\u003e {\n  public onLogin = (evt: any) =\u003e {\n    evt.stopPropagation();\n    evt.preventDefault();\n    // ActionHandler in Model is triggered by dispatch action\n    this.props.dispatch(thisModule.actions.login({username: \"\", password: \"\"}));\n  };\n\n  public render() {\n    const {logining} = this.props;\n    return (\n      \u003cform className=\"app-Login\" onSubmit={this.onLogin}\u003e\n        \u003ch3\u003eLogin\u003c/h3\u003e\n        \u003cul\u003e\n          \u003cli\u003e\u003cinput name=\"username\" placeholder=\"Username\" /\u003e\u003c/li\u003e\n          \u003cli\u003e\u003cinput name=\"password\" type=\"password\" placeholder=\"Password\" /\u003e\u003c/li\u003e\n          \u003cli\u003e\u003cinput type=\"submit\" value=\"Login\" disabled={logining} /\u003e\u003c/li\u003e\n        \u003c/ul\u003e\n      \u003c/form\u003e\n    );\n  }\n}\n\nconst mapStateToProps = (state: RootState) =\u003e {\n  return {\n    logining: state.app.loading.login !== LoadingState.Stop,\n  };\n};\n\nexport default connect(mapStateToProps)(Component);\n```\n\nAs you can see from the above code, View is a Component. Is there any difference between View and Component? No coding, logically there are:\n\n- View embodies the view presentation of ModuleState, which emphasizes the specific business logic, so its props are usually connected directly to the store with mapStateToProps connect.\n- Component represents a pure component without business logic context, and its props are generally derived from parent delivery.\n- Components are usually public, while views are not.\n\n## Routing and Dynamic Loading\n\nReact-coat agrees with the idea of react-router 4 modular routing. Routing is a component. Nested routing is as simple as nested component, without complicated configuration. Such as: `PhotosView` and `VideosView` come from Photos module and Videos module respectively. They are loaded asynchronously on demand.\n\n```JS\nimport {BottomNav} from \"modules/navs/views\"; // BottomNav come from another module\nimport LoginForm from \"./LoginForm\"; // LoginForm come from this module\n\n// PhotosView an VideosView come from other module\nconst PhotosView = loadView(moduleGetter, ModuleNames.photos, \"Main\");\nconst VideosView = loadView(moduleGetter, ModuleNames.videos, \"Main\");\n\n\u003cdiv className=\"g-page\"\u003e\n    \u003cSwitch\u003e\n        \u003cRoute exact={false} path=\"/photos\" component={PhotosView} /\u003e\n        \u003cRoute exact={false} path=\"/videos\" component={VideosView} /\u003e\n        \u003cRoute exact={true} path=\"/login\" component={LoginForm} /\u003e\n    \u003c/Switch\u003e\n    \u003cBottomNav /\u003e\n\u003c/div\u003e\n```\n\nSeveral other views are nested in one of the above views in different loading modes:\n\n- BottomNav is a view of navsModule. Direct import means that it will be loaded into this view synchronously.\n- LoginForm is a view of this module, so it is directly import by relative paths and nested directly, which means it will load synchronously.\n- PhotosView and VideosView come from other modules, but they are acquired through loadView() and nested by Route, which means that they will load asynchronously on demand. Of course, you can also `import {PhotosView} from \"modules/photos/views\"` to load on demand synchronously.\n\nTherefore, the framework is flexible and simple to load modules and views without complex configuration and modification.\n\n- Whether synchronous, asynchronous, on-demand, dynamic loading, only to change the loading mode, without modifying the module.The module itself does not need to formulate in advance who and how it will be loaded to ensure the independence of the module.\n- As mentioned earlier, view is the presentation of model data. When embedding other module views, do you need to import other module models? No, the framework will be imported automatically.\n\n## Several special actions\n\n- **@@router/LOCATION_CHANGE**：The framework integrates connected-react-router, which dispatches the action when the routing changes, and you can subscribe the action in moduleHandlers.\n- **@@framework/VIEW_INVALID**：This action is dispatched when the routing changes, or when any view has Mount or Unmount behavior, which more accurately reflects view updates than @@router/LOCATION_CHANGE\n- **@@framework/ERROR**: The framework catches uncatched error and automatically dispatches this action when errors occur. You can monitor this action in moduleHandlers\n- **module/INIT**：This action is dispatched when the module is first loaded to inject the initial moduleState into the store\n- **module/LOADING**：This action is dispatched when loading progress is triggered, such as @effect(\"login\")\n\n## Roadmap\n\n- react hooks\n\nwith the API unchanged, React Hooks will be used to replace Redux and React-Redux to facilitate user's senseless upgrade.\n\n- react-shirt\n\nwith the same API, Mobx will be used to replace Redux.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwooline%2Freact-coat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwooline%2Freact-coat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwooline%2Freact-coat/lists"}