Page and application JavaScript structure
In this document, we'll go over how a page on VA.gov is related to the application code located in vets-website
.
JavaScript bundles and static pages
If you look at the source code of the /my-va
page when running locally, you'll see a group of three script tags:
<script defer nomodule="" src="/generated/polyfills.entry.js"></script>
<script defer src="/generated/vendor.entry.js"></script>
<script defer src="/generated/dashboard.entry.js"></script>
The vendor
and polyfills
bundles are on every page, but the dashboard
bundle is specific to this React app. In the source code of the /my-va
page, you'll also see this front matter block:
---
title: My VA
layout: page-react.html
entryname: dashboard
---
When this page is built, the entryname
property tells our build process how to construct the path of the third script tag in the above group. For pages that don't have an entryname
, the static-pages
bundle will be used.
In vets-website
, there will be a manifest.json
file with a matching entry name. In this case we can search for "entryName": "dashboard"
and find src/applications/personalization/dashboard/manifest.json. The entryFile
property in the manifest will tell you the entry point of this application, which is the file that Webpack uses as its entry for creating the dashboard
bundle.
Structure of a page
A typical application page has several independent React components. Here's an abbreviated screenshot of the /my-va
page:
Each of the areas marked with a red box is a separately mounted React component. These React components share a common Redux store:
Static content pages work similarly, except the main content area is not a React component (though it may have one or more React widgets mounted within it).
As we covered in the last section, /my-va
uses the dashboard
bundle, the entry file of which is located here. There's not a lot of code in that file. Most of the functionality is abstracted in the startApp function. The dashboard passes in a reducer
and routes
, which is the primary application. In the startApp
function, we create the common Redux store, start the site-wide
components, and mount the primary React application:
The components in the yellow boxes are the common site-wide
components started by startSitewideComponents, and the blue box is the primary React application.
Structure of a React application
Our React applications can vary in structure depending on what their purpose is, but typically they have a reducer
and actions
for Redux related logic, and routes and components for the rest of the application. Routes are typically the best place to start looking at an application. Again using /my-va
as an example, you can see the React components to start looking at in the routes.jsx
file:
import DashboardApp from './containers/DashboardApp';
import DashboardAppNew from './containers/DashboardAppNew';
import DashboardAppWrapper from './containers/DashboardAppWrapper';
import SetPreferences from '../preferences/containers/SetPreferences';
import environment from 'platform/utilities/environment';
const component = environment.isProduction() ? DashboardApp : DashboardAppNew;
export const findBenefitsRoute = {
path: 'find-benefits',
component: SetPreferences,
key: 'find-benefits',
name: 'Find VA benefits',
};
const routes = {
path: '/',
component: DashboardAppWrapper,
indexRoute: { component },
childRoutes: [findBenefitsRoute],
};
export default routes;
There are only two routes
in here, but you can see that there's a main <DashboardAppWrapper/ >
component and then a <DashboardApp />
component for the main index route.
<DashboardAppWrapper />
has some important functionality that's common to a lot of our applications. Here's the render method from that component:
render() {
return (
<RequiredLoginView
serviceRequired={[backendServices.USER_PROFILE]}
user={this.props.user}
>
<DowntimeNotification
appTitle="user dashboard"
dependencies={[
externalServices.mvi,
externalServices.mhv,
externalServices.appeals,
]}
render={this.renderDowntimeNotification}
>
<Breadcrumbs>
{this.renderBreadcrumbs(this.props.location)}
</Breadcrumbs>
{this.props.children}
</DowntimeNotification>
</RequiredLoginView>
);
}
Because /my-va
requires a user to be signed in, we wrap all the child content with <RequiredLoginView />
, which makes sure there's a user signed in, and directs the user to sign in if not. Since the dashboard uses some internal VA services that may be down at times, it also wraps content in a <DowntimeNotifications />
component, which will display a message to a user if one of the specified services is down. See the downtime notifications documentation for more information.
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.