A10 GUI Framework
Why do need to use A10's GUI framework
To provide consistency, reusability and cost savings for the construction of web UI across A10’s product portfolio, we need a common set of technologies, design guidelines, and software libraries.
Setup Issues
How do I install?
npm i -S https://github.com/a10networks/a10-gui-framework.git
Coding issues
1.Coding Style
This project code follows the Prettier
coding style. Developers do not need to care of coding style consistency as long as they are using VSCode
editor and have installed Prettier
plugin. VScode
will format your code automatically through Prettier when saving file due to .vscode/settings.json
.
{
"editor.formatOnSave": true
}
How do I use Redux with the framework?
1. Create action function
import invariant from 'invariant'
import { A10ACTION_TYPES } from '../actionTypes'
export interface IDataSetOptions {
namespace: string | string[]
data: any
containerID: string
}
export interface IDataSetAction extends IDataSetOptions {
type: string
}
export const setData = (options: IDataSetOptions) => {
invariant(options.namespace, 'options.namespace is required')
invariant(options.hasOwnProperty('data'), 'options.data is required')
invariant(options.containerID, 'options.containerID is required')
return {
type: A10ACTION_TYPES.DATA.SET,
...options,
}
}
export interface IDataRemoveOptions {
namespace: string | string[]
containerID?: string
}
export interface IDataRemoveAction extends IDataRemoveOptions {
type: string
}
export const removeData = (options: IDataRemoveOptions) => {
invariant(options.namespace, 'options.namespace is required')
return {
type: A10ACTION_TYPES.DATA.REMOVE,
...options,
}
}
2. Create the reducer function
import { Map, Set } from 'immutable'
import { A10ACTION_TYPES } from '../actionTypes'
import { IDataSetAction, IDataRemoveAction} from '../actions/data'
export const A10Data = (state = Map(), action: any) => {
switch (action.type) {
case A10ACTION_TYPES.DATA.SET: {
const { data, namespace } = action as IDataSetAction
const namespaceArray = Array.isArray(namespace)
? namespace
: namespace.split('.')
return state.setIn(namespaceArray, data)
}
case A10ACTION_TYPES.DATA.REMOVE: {
let { namespace } = action as IDataRemoveAction
namespace = Array.isArray(namespace) ? namespace : [namespace]
namespace.forEach((ns: string) => {
const nsArray = ns.split('.')
state = state.deleteIn(nsArray)
nsArray.pop()
while (nsArray.length) {
const node = state.getIn(nsArray)
if (Map.isMap(node) && node.isEmpty()) {
state = state.deleteIn(nsArray)
}
nsArray.pop()
}
})
return state
}
default:
return state
}
}
3. Store / call the reducer
import {
Store,
createStore as reduxCreateStore,
compose,
bindActionCreators as reduxBindActionCreators,
Dispatch,
ActionCreator,
Action,
} from 'redux'
import { createEpicMiddleware, combineEpics, Epic } from 'redux-observable'
import _ from 'lodash'
import { A10Epics } from './epics'
import * as A10Reducers from './reducers'
export interface IA10ReduxProps {
reducers?: IObject
initState?: IObject
}
export interface IA10ReduxPlugin {
plug?(): IA10ReduxProps
play?(store?: Store<any>): void
}
export type Plugins = Array<IA10ReduxPlugin | IA10ReduxPlugin[]>
export const createStore = (
appReducers: IObject = {},
data: IObject = {},
pluginsList: Plugins = [],
) => {
const plugins = pluginsList.reduce((list: IA10ReduxPlugin[], item) => {
const plugin = Array.isArray(item) ? item : item ? [item] : []
return [...list, ...plugin]
}, [])
const reduxProps = invokePlugins(plugins as IA10ReduxPlugin[], 'plug')
const rootReducer = combineReducers({
...appReducers,
...A10Reducers,
...reduxProps.reducers,
})
const store = reduxCreateStore(
rootReducer,
{
...data,
...reduxProps.initState,
},
)
return store
}
4. Using the component
import React from 'react'
import { Store } from 'redux'
import { Provider as ReduxProvider } from 'react-redux'
import _ from 'lodash'
import { A10Root } from './A10Root'
import { CONFIG, IGlobalConfig } from '../settings/config'
import { createStore, checkNamespaces, IA10ReduxProps, Plugins } from '../redux'
import { getHTTPManager } from '../libs'
export interface IA10ProviderProps extends IA10ReduxProps {
CONFIG: IGlobalConfig
store?: Store<any>
plugins?: Plugins
}
export interface IEpicDependencies {
httpClient: IGlobalConfig['httpClient']
HTTPManager: ReturnType<typeof getHTTPManager>
}
export class A10Provider extends React.Component<IA10ProviderProps> {
static defaultProps = {
CONFIG,
}
render() {
const {
reducers,
middlewares,
initState,
epics,
epicDependencies,
plugins,
children,
CONFIG: APP_CONFIG,
store: providedStore,
} = this.props
// make GLOBAL_CONFIG and CONFIG are same reference for global imported getNS()
const GLOBAL_CONFIG = Object.assign(CONFIG, APP_CONFIG)
GLOBAL_CONFIG.EPIC_DEPENDENCIES = GLOBAL_CONFIG.EPIC_DEPENDENCIES || {}
const { EPIC_DEPENDENCIES } = GLOBAL_CONFIG
// backward compatibility support
if (!GLOBAL_CONFIG.httpClient && EPIC_DEPENDENCIES.httpClient) {
GLOBAL_CONFIG.httpClient = EPIC_DEPENDENCIES.httpClient
} else {
EPIC_DEPENDENCIES.httpClient = GLOBAL_CONFIG.httpClient
}
if (GLOBAL_CONFIG.DEBUG && _.isPlainObject(GLOBAL_CONFIG.REDUX_DATA_NS)) {
checkNamespaces(GLOBAL_CONFIG.REDUX_DATA_NS)
}
Object.assign(EPIC_DEPENDENCIES, epicDependencies)
const { httpClient } = GLOBAL_CONFIG
const HTTPManager = getHTTPManager(httpClient)
EPIC_DEPENDENCIES.HTTPManager = EPIC_DEPENDENCIES.HTTPManager || HTTPManager
const store =
providedStore ||
createStore(
reducers,
middlewares,
epics,
initState,
EPIC_DEPENDENCIES,
plugins,
)
return (
<ReduxProvider store={store} {...this.props}>
<A10Root CONFIG={GLOBAL_CONFIG} store={store} HTTPManager={HTTPManager}>
{children}
</A10Root>
</ReduxProvider>
)
}
}
export default A10Provider
How do I use redux-observable in the framework?
1.install rxjs
and redux-observable
.
rxjs
and redux-observable
.npm i -S rxjs redux-observable
2.Create epic functions
// approot/redux/epics/myEpic.ts
import { Observable } from 'rxjs'
import { ActionsObservable } from 'redux-observable'
import { Action, Store } from 'redux'
import { setMyActionData, IMyAction } from 'approot/redux/actions' // actionCreators
import { ACTION_TYPES } from 'approot/redux/actionTypes'
export const myActionEpic = (
action$: ActionsObservable<Action>,
store: Store<any>,
epicDependencies: IObject,
): Observable<Action> => {
// action in
return action$
.ofType(ACTION_TYPES.MY_ACTION)
.mergeMap((action: IMyAction) => {
const { params } = action
const { httpClient, HTTPManager } = epicDependencies
return (
Observable.from(httpClient.get('/axapi/v3/test', params))
// action out
.map((response: IObject) =>
setMyActionData({
response,
}),
)
// action out for exception
.catch((error: IObject) =>
Observable.of(
setMyActionData({
error: true,
}),
),
)
)
})
}
3. Collect all epic functions and then export it in epics/index.ts.
// approot/redux/epics/index.ts
import { myActionEpic } from './myEpic'
export const epics = [myActionEpic]
export default epics
4.Provide the epics to A10Provider.
// approot/index.ts
import React from 'react'
import ReactDOM from 'react-dom'
import { A10Provider, A10Router } from 'a10-gui-framework'
import CONFIG from 'approot/configs/global'
import middlewares from 'approot/redux/middlewares'
import reducers from 'approot/redux/reducers'
import epics from 'approot/redux/epics'
import APP from 'approot/containers/App'
const initState = {}
ReactDOM.render(
<A10Provider
CONFIG={CONFIG}
middlewares={middlewares}
reducers={reducers}
epics={epics}
initState={initState}
>
<A10Router.Browser>
<APP />
</A10Router.Browser>
</A10Provider>,
document.getElementById('root'),
)
Can I use redux-thunk?
Yes, redux-thunk is an asynchronous action middleware.
What are the benefits of using httpRequest?
All React-Redux applications need to communicate with an HTTP server. A set of common problems frequently arise while making HTTP requests: error handling, request cancellation, and state data management. Here are some proposals for dealing with this problems.
Generic Error Handling
GUI Framework will catch every HTTP error and program error at runtime. It analyzes the error, and dispatches out a Redux action with complete error information and appropriate error message (JSON syntax or pure text) that could be customized by app developer if needed.
Request Cancellation
Every HTTP request is able to be aborted as long as developer dispatches out a cancel action. However, if the request may affect server state change, then the server state is undefined. This cancellation facility is often used when switching pages or to stop UI operations (mainly HTTP GET requests).
State Data Management
Most developers may face issues arising from state data management in Redux store. Redux will not purge unused state data in the store by default, which may introduce unnecessary memory consumption.
Last updated
Was this helpful?