Generated source code

Let's take a look at the generated code. Since we looked at the server-side code in the previous chapters, we will only look at the client-side code here:

The structure is quite similar to what we saw for Angular, but the React code is organized slightly differently. Here, we are only concerned with the code inside src/main/webapp/app since everything else is exactly the same as what we saw for the Angular application.

Let's take a look at some of the important parts of the code:

  • index.tsx: This is the entry point of our application. This is where we Bootstrap React to the root div and initialize the Redux store:
...
const store = initStore();
registerLocale(store);

const actions = bindActionCreators({ clearAuthentication }, store.dispatch);
setupAxiosInterceptors(() => actions.clearAuthentication('login.error.unauthorized'));
...
const rootEl = document.getElementById('root');

const render = Component =>
ReactDOM.render(
<ErrorBoundary>
<Provider store={store}>
<div>
...
<Component />
</div>
</Provider>
</ErrorBoundary>,
rootEl
);

render(AppComponent);
  • app.tsx: This is our main application component. We declare the React Router and the main application UI structure here:
...
export const App = (props: IAppProps) => {
...
return (
<Router basename={baseHref}>
<div className="app-container" style={{ paddingTop }}>
<ToastContainer ... />
<ErrorBoundary>
<Header
...
/>
</ErrorBoundary>
<div className="container-fluid view-container"
id="app-view-container">
<Card className="jh-card">
<ErrorBoundary>
<AppRoutes />
</ErrorBoundary>
</Card>
<Footer />
</div>
</div>
</Router>
);
};
...
  • routes.tsx: This is where the application's main parent routes are defined. They are imported into the app.tsx file from here. We looked at this component earlier.
  • config: This is where framework-level configurations are done. Some important ones are as follows:
    • axios-interceptor.ts: HTTP interceptors are configured here. This is where the JWT tokens are set to requests and errors are handled.
    • constants.ts: Application constants.
    • *-middleware.ts: The error, notification, and logging middleware for Redux is configured here.
    • store.ts: Redux store configuration is done here. Middlewares are registered during this stage. The order of the middlewares in the array is important as they act as a pipeline, passing actions from one middleware to another, as shown here:
const defaultMiddlewares = [
thunkMiddleware,
errorMiddleware,
notificationMiddleware,
promiseMiddleware(),
loadingBarMiddleware(),
loggerMiddleware
];
    • translation.ts: i18n-related configurations are done here.
  • entities: The entity modules are present here.
  • modules: The application UI modules are here:
    • account: Account pages such as settings and password reset can be found here.
    • administration: The admin screens, such as metric, health, and user management, are here.
    • home: Home screen of the application.
    • login: Login and logout components.
  • shared: Shared components and reducers:
    • authprivate-route.tsx: This is used for authenticated routes.
    • error: Custom error boundaries used in the application.
    • layout: Layout-related components such as header, footer, and menu.
    • model: TypeScript model for entities.
    • reducers: Shared reducers used by the application:
      • authentication.ts: This is used for authentication-related actions and reducers. Let's take the LOGIN action as an example. This action accepts username, password, and remember me and dispatches ACTION_TYPES.LOGIN with an asynchronous payload from an HTTP call to authenticate our credentials. We use the async/await feature from ES7 to avoid complex callbacks here. The result from the dispatch is obtained when we extract the JWT bearerToken and store it in the local or session storage of the browser, based on the remember me setting that's passed. Dispatching ACTION_TYPES.LOGIN will trigger the appropriate case in the reducer based on the status of the Promise:
...

export const ACTION_TYPES = {
LOGIN: 'authentication/LOGIN',
...
};

const initialState = {
...
};

// Reducer
export default (state = initialState, action) => {
switch (action.type) {
case REQUEST(ACTION_TYPES.LOGIN):
case REQUEST(ACTION_TYPES.GET_SESSION):
return {
...state,
loading: true
};
case FAILURE(ACTION_TYPES.LOGIN):
return {
...initialState,
errorMessage: action.payload,
showModalLogin: true,
loginError: true
};
...
case SUCCESS(ACTION_TYPES.LOGIN):
return {
...state,
loading: false,
loginError: false,
showModalLogin: false,
loginSuccess: true
};
...
default:
return state;
}
};
...
export const login =
(username, password, rememberMe = false) => async (dispatch, getState) => {
const result = await dispatch({
type: ACTION_TYPES.LOGIN,
payload: axios.post('/api/authenticate', {
username, password, rememberMe })
});
const bearerToken = result.value.headers.authorization;
if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') {
const jwt = bearerToken.slice(7, bearerToken.length);
if (rememberMe) {
Storage.local.set(AUTH_TOKEN_KEY, jwt);
} else {
Storage.session.set(AUTH_TOKEN_KEY, jwt);
}
}
await dispatch(getSession());
};
...
    • util: Utility functions used in the application.

The folder structure of the unit test code is also quite similar to the main src folder:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.76.0