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