Welcome to my Boilerplate for React projects.
First and foremost, it is important to mention that all sites can and should build with an accessibility first mindset. https://www.w3.org/WAI/fundamentals/accessibility-usability-inclusion/
This project contains everything needed to quickly bootstrap a new UI project. You only need to pull it down and replace the naming and URLs in a few areas, but otherwise it will work out of the box.
You will need Node installed locally. You can use any OS and IDE that you wish.
The following steps in this guide will show you how to create a new project using this boilerplate as a template, and give an overview of the structure and architecture of the application itself.
-
Open
package.json
and replace the following fields with the name and location of your new project instead:(name, description, repository)
-
Open
webpack.config.js
and change thetitle
from R/R Boilerplate to your new project name. -
Build and run locally to test everything is working correctly. Refer to the Build and Test section.
-
Finally, check this in to your master branch as the initial commit for the repository.
-
Make sure you have node and NPM installed
-
Update your node modules by running
npm install
-
Run
npm run dev
to start up the dev server -
Navigate to
localhost:3002
in order to verify the project is up and running without issues
The project configuration consists of the main package.json
, webpack.config.js
, babel.config.js
, and .npmrc
- The
package.json
is the main entry point for an NPM project, consisting of basic project information, the compile time and run time dependencies used in the project, miscellaneous configuration, and a scripts object. The scripts are used to build, lint, and test the application.
-
The
dev
script should be used to run the application locally, spinning up a local development server hosting your built react project -
The
build
script is an environment agnostic build using Webpack that will generate files in thedist
folder for deploy. -
The lint scripts are used for linting the JavaScript and SCSS/CSS in the project
-
Other script in there can be inferred by their content
-
Packages in the
package.json
are used for various reasons. The runtime, meaning needed for the app while running, should be located in thedependencies
section, and the compile time in the other section. -
The
webpack.config.js
file contains all the configuration needs to run the app locally and build for production. Themodule.exports
section contains the entry point of the application, the output variables for building the app, and modules used to transpile, load resources, and translate. -
The
babel.config.js
file is used by any scripts running babel, which is used for transpiling the code, to run through presets and plugins, as well as target environments such as react and browser versions. The browser versions are listed in the.browserslistrc
file. -
The
.npmrc
file is used to connect to our private NPM feed on Azure.
Linting is used to ensure common standards are being followed for our JavaScript and style code within the project. The linters use eslint and stylelint with plugins to work. The scripts in the package.json
can be used to run them manually on your code. The linters will also run automatically on commit locally, as well as when you have a pull request up against an official branch.
-
The
.eslintrc
file contains rules that inherit from the standard and standard-jsx JavaScript rule sets. There is also a plugin fora11y
to ensure adherence to accessibility rules. -
The
.stylelintrc
file contains rules extending thestylelint-config-standard
rule set. There are a few extra rules defined as well. This will ensure standards followed on SCSS and CSS code written.
-
The react code is all housed in the
app
folder. -
Inside this folder, all the rest of the folders are organized in a flat structure with regard to function and a clear separation of concerns between libraries for ease of extension and replacement.
-
The main functions in the app are
state management
,side-effects
,services
,translations
, andreact code
. -
The store and reducers folders contain the state management.
-
The sagas folder contains the means for capturing side effects as a middleware.
-
The components folder holds all of the react code that is not entry and set up.
-
The rest of the folders should be intuitive enough to decipher, as everything is laid out to be as easy to find as possible.
- The entry of the react project occurs in the
index.js
of the main app folder. In this file, top level configuration is being created and the react app itself is being mounted.
-
The
whatwg-fetch
is a polyfill for the fetch API on browsers, used in our services. -
The store is imported from the store folder. The store folder contains the create file which is used to combine the reducers and middleware together to form a single store for state management. The middleware file is to separate the concern of middleware creation and contains set up for the sagas, and some development helpers for logging redux actions and invariant trackers.
-
The service index is imported to mount all the service files and have them register themselves, which will be touched upon in the services section.
-
The sagas are imported in a similar fashion so they can all mount before component render.
-
The translator used in the application is initialize.
-
The history object is created to give access to all components rendered within the provider.
-
Finally,
ReactDOM.render
is called on the imported App component, with Provider and Router wrapped around it, and mounting on the div with the idapp
in theindex.html.ejs
file. The Provider is used to provide the store to all components it wraps. The Router provides access to the router for all child components as well.
- The
index.html.ejs
file is the main wrapper for the application, where the react components are mounted.
-
This file contains global styles and static assets used such as the font awesome library, bootstrap css, the bundles styling for the react app.
-
The
core-js
andruntime
JS files are imported as polyfills. -
The
config.js
is referenced here as a deployed file, for runtime configuration values. -
The app's bundled JS code is imported based on the generated hash file created by the webpack build process.
- The main routing in the application occurs in the
App
component, with declarative routing usinguseRouteMatch
or theRoute
component.
-
The paths should be exact unless you require multiple components rendered using routes, such as a persistent header or side navigation along with the content of other components.
-
Each component that should update on route change is provided a key that is the pathname of the current location in order to force re-render.
-
The app is wrapped in the
main
tag as per accessibility guidelines, followed by a bootstrapcontainer
. All direct children of a bootstrap container need to berows
and all direct children of rows need to becols
. This is to ensure proper containerization and responsiveness that bootstrap handles out of the box. -
Further Rows and Columns are optional beyond that point, but the same rules will apply. They can be used to create responsive views on a more granular level as needed.
-
The authentication process relies on the user logging in through the main log in process and stores the authentication and refresh tokens in cache in the fetch.js utility file.
-
The services reference the
getAuth
function and set the result into the request header when making API calls.
-
The function will check for the token in local storage first. If it's not there, and you're not on your local environment, it will redirect you to the log in page.
-
If all is well by now, it will return that token back to the service or whatever called it to fetch the token.
- There are other helper functions in here as well, such as the logout function to easily log the user out programmatically.
- The service functions are located in the
service
folder.
-
Each service is a standalone, with the same structure. They contain an export of their own name for reference, the URL they call out to, a mock function, the service function itself, and a default export registering itself using the
async-operations
library. -
The
async-operations
library is used to unify the creation and use of services. It provides aregisterOperation
function which accepts the service name, function, and mock function, and returns an object representation for use in dispatching it as an action, invoking it directly, and reference it's named actions for side effects. Refer to the documentation -
The service function contains the logic for the service call using the fetch API. It constructs the options using the helper function
getDefaultOptions
from thefetch.js
file, which calls the helper function in theauhtentication.js
file for the token. -
The method and body (if necessary) are also outlined in the options.
-
The fetch API is called using the URL and options.
-
The helper function
deserializeJsonResponse
is used on the response to get a usable JSON object. -
Any errors are propagated using the
throwExceptionErrors
helper function. -
Finally the de-serialized response is returned to the caller.
-
The mock function can be used in any number of ways to mock data as needed on success and failure responses using the
enableMock
function from theasync-operations
library. This function is exposed on thewindow
object for ease of access when testing locally.
- The services are utilized in the container files of the components. There are hooks in the hooks folder called
useService
anduseServiceCommand
to help.
-
In the container, to call the service on render, you can utilize the
useService
hook. It will mount auseEffect
with the provided service function and parameters, and return a loading and error variable that will change as the service processes and completes. -
The
useServiceCommand
hook can be use to set up a service that you can invoke as needed, in case of use in a side effect or click handler in the container.
-
Redux is used as state management middleware for the application. Please refer to the Redux documentation for information on the lifecycle, as well as structure of reducers and actions.
-
The reducers that represent the state are located in the
reducers
folder. Theindex.js
within this folder combines them into a single state object, with each reducer being keyed off the name used. -
Each reducer has the same layout, which include a default function export that accepts the current state and the action. The type of the action is parsed and if it matches an action the current reducer cares about, we pass the state and action into a function to update the current state.
-
All action creator functions and action constants are exported out of the reducer itself.
-
All selectors are also exported out of the reducer to be used in component containers to access state.
-
Inside a container, you will use
useSelector
fromreact-redux
and pass in a function that selects the state you want. This can either be in the form ofstate => state.name.value
or importing a previously created selector function from the relevant reducer.
-
Side effects are mostly handled by utilizing the
useEffect
hook in React and capturing and producing side effects with sagas. -
Sagas as well as other middleware are set up in the
middleware.js
file. Invariant is used to warn of mutation of state during development. Loggers are used to log redux actions to the console and state change tracking. We also set theenableMock
anddisableMock
functions on the window object from theasyncOperations
project. -
There is a top level saga that is mounted and runs for the
asyncOperations
project, and it listens for the actions of services and dispatches success and failure actions on behalf of the service with top level error handling. -
Other sagas can be set up to listen for specific actions or other triggers, and produce side effects asynchronously. Sagas use iterator functions, allowing for an
async/await-like
coding pattern usingyield
.
-
Styling is accomplished using SCSS and bootstrap.
-
The
style
folder contains thevariables.scss
file that is imported into all the component style files, in order to reference our custom bootstrap theme with variables correlating to various color palettes. All colors used must be referenced from here as a variable for use in any styles. -
The components all have their own style files and must reference them as such, with the styles in the file wrapped in a parent class representing the component itself with proper nesting.
-
All written language in the application must be referenced using translations.
-
The
translations
folder contains files correlating to translated languages. -
Within the component file, you can reference the translation by passing in the name of the component key and then get a function you can pass keys for the translations within that component section to get the values.
- Naming Convention
-
Each component has the same structure. The name of the component is the folder that contains the files. Each folder contains at minimum an
index.js
. If you need to access state or produce side effects, you will need acomponent.js
. If you need custom styling you will also have anindex.scss
. Other files can be evaluated as needed if they are specific to the component. -
The
index.js
houses the container or "smart component". If there is no prop access or side effects this can simply be the render component. -
The
component.js
contains the render component or "dumb component". It should simply accept props and render, based on those props. Same props in, same rendered component out each time.
- Styling
-
Bootstrap accomplishes a lot of styling with automatic responsiveness and helper classes to use for each of margin, padding, and various things.
-
If custom styling is needed, it will be housed in the
index.scss
file. Within this file, thevariables.scss
file should be imported to access the custom theme and colors, and all styles will be wrapped in a parent class that is present on the top level tag inside the component file. All styles should be based on classes with proper nesting. If the styles are not nested in a parent class, they could bleed and affect other components across the app as the styles are bundled into a single file.
- Container Structure and Practices
-
If needed, the container is housed in the
index.js
file. The name of the component should be the "ComponentName" + Container, e.g.AppContainer
. -
The render component should be imported from the
component.js
and returned with all necessary props passed into it. -
All state is access using
useSelector
fromreact-redux
and state functions. It can also be created and tracked usinguseState
as needed. -
Service calls and side effects are handled in here as well using
useService
,useServiceCommand
, anduseEffect
. -
All handler and callback functions should be set up in here using
useCallback
or simple curried functions.
- Component Structure and Practices
-
The
component.js
or the file containing the render component will accept props and use those to render the component. Whatever props are needed for rendering should be outline in the Component.propTypes object at the bottom of the file. -
Utilize bootstrap for styling and responsiveness as needed
-
The components should be broken up into small functional units within the file, for ease of extensibility and readability.
- Forms are handled using a hooks based approach. An example usage is in the Login component.
-
Loading should be done on initial renders and any side effects where the component needs the results or a service call or some other asynchronous function.
-
Pass in a single loading prop into the render component and use a loader. If it is a top level loading sequence, use the
Loader
component. However, if individual components need to load on the page without disruption, use theShadowLoader
component with the preferred type. It will automatically scale based on screen size.
The main branch for this repository is master
Feel free to submit pull requests for any standards you'd like to see implemented in our front end projects.