A lightweight, configured initiator
Warehouse address: github.com/myNameIsDu/…
Install
npm install @aktiv/launcher
Copy the code
Introduction
Before that, your React project may need to install react-Router React-router-dom Redux React-Redux Redux-Thunk… Now you don’t need any of that. All you need is a Launcher, which comes with a Launcher and provides a simple, configurable development solution for viewing versions of the built-in library
In addition, it provides a set of plug-in processes to make applications more reusable and extensible
Get Started
import Launcher from '@aktiv/launcher';
const Home = () = > {
return <div>home</div>;
};
const About = () = > {
return <div>home</div>;
};
const RouterConfig = [
{
path: '/'.component: Home,
},
{
path: '/about'.component: About,
},
];
const app = new Launcher({
routes: RouterConfig,
});
app.start();
Copy the code
It has only one mandatory parameter, which is your router configuration
Overall, the parameters fall into two categories: Router and Store
interfaceConstructorOptionsType { hash? :boolean;
routes: Array<RouteItem>; reducerConfig? : ReducerConfig; reducers? : ReducersMapObject; immerEnableES5? :boolean;
}
Copy the code
router
hash
The default value is browserRouter. Use the hash parameter to enable the hashRouter
routes
Configuring static Routes
interfaceRouteItemBase { path? :string; casessensitive? :boolean; children? :Array<RouteItem>; lazy? :boolean; title? :string;
}
export interface RouteItem extendsRouteItemBase { redirect? :string; component? : ComponentType | (() = > DynamicImportType);
// plugin route options
[x: string]: unknown;
}
Copy the code
We extended the Lazy, title, and redirect options from the React-Router
lazy
The lazy loading mode is enabled
title
When you load the current route, the launcher automatically assigns you the value document.title
redirect
Since react-Router V6 no longer supports the redirect component, we have built-in redirect functionality here, which falls into three categories:
interfaceRouteItemBase { path? :string; casessensitive? :boolean; children? :Array<RouteItem>; lazy? :boolean; title? :string;
}
export interface RedirectRouteItem extends RouteItemBase {
redirect: string;
}
export interface NormalRouteItem extends RouteItemBase {
component: ComponentType | (() = > DynamicImportType);
}
export interface ParentRedirectRoteItem extends RouteItemBase {
redirect: string;
component: ComponentType | (() = > DynamicImportType);
}
Copy the code
store
The configuration for store can be a little complicated, but first here is our SCR directory
actions
|-list
|-index.js
index.tsx
Copy the code
actions/list
import { createActions } from '@aktiv/launcher';
const state = { // The default value of state
a:"1"
}
const config = {
editorA: {key:'a'.// The object state key is changed when the dispatch is initiated
payload:(data) = > data
}
}
export {
state,
config,
actions: createActions(config)
}
Copy the code
actions/index
import list from './list'
export { list }
Copy the code
index.tsx
import Launcher from '@aktiv/launcher';
import reducerConfig from './actions'
const app = new Launcher({
routes: RouterConfig,
reducerConfig
});
app.start();
Copy the code
This configuration results in the following state
{
list: {
a: "1"
}
}
Copy the code
The use of dispatch:
import { actions as listActions } from './actions/list'
listActions.editorA('hello')
Copy the code
In this case, state is
{
list: {
a: "hello"
}
}
Copy the code
You can also do other things inside payload, such as make a request
import { createActions } from '.. /src';
const state = { // The default value of state
a:"1"
}
const config = {
editorA: {key:'a'.// The object state key is changed when the dispatch is initiated
payload:(data) = > {
return api(data)
}
}
}
export {
state,
config,
actions: createActions(config)
}
Copy the code
Call Dispatch with your state as follows:
{
list: {
a: "apiResponse"}}Copy the code
By default, the return value of payload will only replace the corresponding state key, but you can also customize the processing
import { createActions } from '.. /src';
const state = { // The default value of state
a:"1"
}
const config = {
editorA: {key:'a'.// The object state key is changed when the dispatch is initiated
payload:(data) = > {
return api(data)
},
// To handle state, you just need to add handle, which receives the current state and your payload
handle:(state, payload) = > {
// For example, change state.a according to the API return
if(payload.data === true) {
// Of course, you can also change state.b state.c or whatever state you want to handle
state.a="hello"
} else {
state.a='hi'}}}}export {
state,
config,
actions: createActions(config)
}
Copy the code
Note that immer is built in and produce is used to wrap handle around it, so you only need to assign state directly and you don’t need to return it. Since state is the draft of immer, you need to use immer’s functionality if you want to debug, for example:
import { current } from '@aktiv/launcher';
// We provide all the exports to the built-in library. You only need to export from the launcher
const config = {
editorA: {key:'a'.// The object state key is changed when the dispatch is initiated
payload:(data) = > {
return api(data)
},
// To handle state, you just need to add handle, which receives the current state and your payload
handle:(state, payload) = > {
// debug state
console.log(current(state))
// For example, change the state based on what the API returns
if(payload.data === true) {
// Of course, you can also change state.b state.c or whatever state you want to handle
state.a="hello"
} else {
state.a='hi'}}}}Copy the code
Detailed use can view immer official documentation: immerjs. Making. IO/immer/curre…
Ok, so that’s the general use of store, but there are two other parameters: immerEnableES5 and reducers
ImmerEnableES5: open immer compatible es5, details to view immer official document: immerjs. Making. IO/immer/insta…
Reducers: custom reducers. Note that you want to use immutable data as you normally would and return it, since you don’t use immer to handle it
plugin
The Plugin function is the most powerful feature of the Launcher. It is developed based on the Router. A plugin looks like this
export interface Plugin {
name: string; outer? : PluginOuterRenderType; inner? : PluginInnerRenderType; reducerConfig? : ReducerConfigItem; }Copy the code
The simplest scenario is when the login interface request is successful and you want to show your application and pass on user information
import React, { useEffect, useState } from 'react';
import Launcher from '@aktiv/launcher';
const LoginProviderContext = React.createContext(null);
const LoginProvider = ({ children }) = > {
const [isLogin, setLogin] = useState(false);
const [userInfo, setUserInfo] = useState({});
useEffect(() = > {
loginApi()
.then(res= > {
setLogin(true);
setUserInfo(res.data);
})
.catch(error= > {
redirect('/login'); }); } []);return isLogin ? (
<LoginProviderContext.Provider value={userInfo}>{children}</LoginProviderContext.Provider>
) : null;
};
const loginPlugin = {
name: 'login'.// The first argument is the inner component, and the second argument is the argument passed when use is used
outer: (children, opt) = > {
<LoginProvider opt={opt}>{children}</LoginProvider>; }};const app = newLauncher({... });// Pass the second parameter to the plug-in
app.use(loginPlugin, opt)
app.start()
Copy the code
Of course you can have more than one plug-in, just pay attention to the invocation hierarchy of the plug-in, and those that are called later will be wrapped in the outer layer
outer
The outer example is shown above. The point here is that the outer is wrapped around the router, so you can think of it as globally unique
inner
A common example of inner is per-route control because it is wrapped around each route, such as authentication for each route
import React, { useEffect, useState, useContext } from 'react';
import Launcher, { useLocation } from '@aktiv/launcher';
const AuthContext = React.createContext()
const AuthProvider = ({children}) = > {
const [ authData, setAuthData ] = useState()
useEffect(() = >{
authApi()
.then(res= > {
setAuthData(res.data)
})
},[])
return authData ? (
<AuthContext.Provider value={authData}>{children}</AuthContext.Provider>
) : null;
}
const AuthRouteComponent = ({ children, route }) = > {
const { hasAuth } = route
const { pathname } = useLocation()
const authInfo = useContext(AuthContext)
if(hasAuth && ! authInfo.has(pathname)){return 'No permissions'
}
return children
}
const plugin = {
name:'auth'.outer:(children, opt) = > {
return <AuthProvider opt={opt}>{children}</AuthProvider>
},
inner:(children, route, opt) = > {
return <AuthRouteComponent route={route} opt={opt}>{children}</AuthRouteComponent>}}const Home = () = > <div>home</div>
const About = () = > <div>about</div>
const app = new Launcher({
routes:[
{
path:'/'.component:Home
},
{
path:'/about'.component:Home,
// / About requires authentication
hasAuth:true
}
]
})
app.use(plugin, opt)
Copy the code
reducerConfig
Each plug-in also has the ability to participate in the Store
const authReducerConfig = {
state: {a:1
},
config: {editorA: {key:'a'.payload: data= > data
}
}
}
const plugin = {
name:"auth".inner:... .out:... .reducerConfig: authReducerConfig
}
app.use(plugin)
Copy the code
State the following
{
auth:{
a:1
}
}
Copy the code
Built-in library
"History", "^ 5.0.0", "immer" : "^ 9.0.5", "the react - hot - loader" : "^ 4.13.0", "the react - loadable" : "^ 5.5.0", "the react - redux" : "^ 7.2.4", "the react - the router" : "^ 6.0.0 - beta. 0" and "the react - the router - dom" : "^ 6.0.0 - beta. 0", the "story" : "^ 4.1.0", "the story - thunk" : "^ 2.3.0."Copy the code
In addition, a redux middleware for processing promise based on ReduX-Promise is implemented internally