React conventies - RWS-NL/air-node-packages GitHub Wiki

React Redux Naming Conventions

Table of contents


This document will lay out the conventions that we adhere in the React / Redux code in the frontend of AIR applications. Please take note that this is aimed at what are called "new" frontend apps, written in TypeScript and making use of Redux-Saga as middleware. While these conventions can be applied to the classic frontend of air, some divergencies will have to be made.

Folder Structure

We use a folder structure based on create-react-app in the frontend applications, mixed in with common practices from various online articles as well as our own preferences. The common folder structure is as follows. [Any text between means template for a real file name]:

.
├── .editorconfig
├── .env
├── .env.stubrunner
├── .eslintrc
├── .git-authors
├── .gitattributes
├── .gitignore
├── .stylelintignore
├── .stylelintrc
├── .vscode
│   ├── [other-config].json
│   └── typescriptreact.json.code-snippets
├── Procfile
├── README.md
├── __tests__
│   ├── e2e
│   │   ├── [Test].test.ts
│   │   └── index.test.ts
│   ├── jest-environment.js
│   ├── jest-framework.ts
│   ├── jest-puppeteer-config.ts
│   ├── puppeteer-setup.ts
│   ├── screenshots # Gets autogenerated
│   └── smoke
│       └── smoke.test.ts
├── babel.config.js
├── ci
│   ├── app.yml
│   ├── scripts
│   │   ├── build-and-test.sh
│   │   ├── pr-build-and-test.sh
│   │   └── smoke-test.sh
│   └── tasks
│       ├── build-and-test.yml
│       ├── pr-build-and-test.yml
│       └── smoke-test.yml
├── docker-compose.yml
├── jest.config.js
├── manifest.yml
├── package.json
├── public
│   ├── android-chrome-192x192.png
│   ├── android-chrome-512x512.png
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── scripts
│   ├── [other-helper-script].[ext]
│   ├── run-airfrontend.sh
│   ├── run-backend.sh
│   ├── validateNoOnly.ts
│   └── yarnAfterPull.ts
├── src
│   ├── App.tsx
│   ├── __tests__
│   │   ├── App.test.tsx
│   │   ├── components
│   │   │   ├── [Component].test.tsx
│   │   │   └── presentational
│   │   │       └── [PresentationalComponent].test.tsx
│   │   ├── config
│   │   │   └── i18n.test.tsx
│   │   ├── containers
│   │   │   ├── AppBase.test.tsx
│   │   │   ├── [Container].test.tsx
│   │   │   └── __snapshots__ # Gets autogenerated
│   │   │       ├── [Container].test.tsx.snap # Gets autogenerated
│   │   │       └── AppBase.test.tsx.snap # Gets autogenerated
│   │   ├── store
│   │   │   ├── navigation
│   │   │   │   ├── __snapshots__ # Gets autogenerated
│   │   │   │   │   └── navigationReducer.test.ts.snap # Gets autogenerated
│   │   │   │   └── navigationReducer.test.ts
│   │   │   ├── oauth2
│   │   │   │   ├── StateProvider.test.ts
│   │   │   │   ├── __snapshots__ # Gets autogenerated
│   │   │   │   │   └── oauth2Reducer.test.ts.snap # Gets autogenerated
│   │   │   │   ├── oauth2ImplicitFlow.test.ts
│   │   │   │   ├── oauth2Reducer.test.ts
│   │   │   │   └── oauth2saga.test.ts
│   │   │   └── [storeSection]
│   │   │       ├── __snapshots__ # Gets autogenerated
│   │   │       │   └── [test].test.ts.snap # Gets autogenerated
│   │   │       ├── [storeSection]Reducer.test.ts
│   │   │       └── [storeSection]Sagas.test.ts
│   │   ├── stubrunner.test.ts
│   │   └── testSupport.tsx
│   ├── components
│   │   ├── [Component].tsx
│   │   └── presentational
│   │       └── [PresentationalComponent]
│   ├── config
│   │   ├── Router.tsx
│   │   ├── i18n
│   │   │   ├── en.json
│   │   │   └── nl.json
│   │   ├── i18n.ts
│   │   ├── localoauth2.json
│   │   ├── mime.types
│   │   ├── nginx.conf
│   │   └── routes.ts
│   ├── containers
│   │   ├── AppBase.tsx
│   │   └── [Container].tsx
│   ├── contexts
│   │   ├── [OtherContext].ts
│   │   ├── NavigationContext.ts
│   │   └── PermissionContext.ts
│   ├── index.tsx
│   ├── react-app-env.d.ts
│   ├── setupTests.ts
│   ├── store
│   │   ├── index.ts
│   │   ├── navigation
│   │   │   ├── navigationActions.ts
│   │   │   ├── navigationReducer.ts
│   │   │   └── navigationTypes.ts
│   │   ├── oauth2
│   │   │   ├── StateProvider.ts
│   │   │   ├── oauth2Actions.ts
│   │   │   ├── oauth2ImplicitFlow.ts
│   │   │   ├── oauth2Reducer.ts
│   │   │   ├── oauth2Saga.ts
│   │   │   └── oauth2Types.ts
│   │   ├── [storeSection]
│   │   │   ├── [storeSection]Actions.ts
│   │   │   ├── [storeSection]Reducer.ts
│   │   │   ├── [storeSection]Sagas.ts
│   │   │   └── [storeSection]Types.ts
│   │   ├── root-action.ts
│   │   ├── root-reducer.ts
│   │   ├── root-sagas.ts
│   │   ├── types.d.ts
│   │   └── utils.ts
│   ├── styles
│   │   ├── font
│   │   │   ├── heading
│   │   │   │   └── regular
│   │   │   │       └── rijksoverheidsansheading-regular_2_0-webfont.woff
│   │   │   └── text
│   │   │       ├── bold
│   │   │       │   └── rijksoverheidsanswebtext-bold-webfont.woff
│   │   │       ├── italic
│   │   │       │   └── rijksoverheidsanswebtext-italic-webfont.woff
│   │   │       └── regular
│   │   │           └── rijksoverheidsanswebtext-regular-webfont.woff
│   │   ├── images
│   │   │   ├── icons
│   │   │   │   └── [name].svg
│   │   │   └── [name].svg
│   │   ├── modules
│   │   │   ├── Common.module.scss
│   │   │   ├── [Component].module.scss
│   │   │   ├── OAuth2.module.scss
│   │   │   └── polyfills.module.scss
│   │   ├── styles.scss
│   │   └── variables
│   │       ├── _colors.scss
│   │       └── _vars.scss
│   └── utils
│       ├── WindowLocationService.ts
│       ├── apiClient.ts
│       ├── apiPaths.ts
│       ├── constants.ts
│       ├── helpers.ts
│       └── linkWithParams.ts
├── tsconfig.eslint.json
├── tsconfig.json
└── yarn.lock

File Naming

  • datadropActions, datadropReducer etc.
  • PascalCase Components
  • camelCase typeScript code + index.tsx
  • ComponentName.Module.scss for SCSS modules
  1. Any Redux related file starts with its subsection of the store (for example users or projects) followed by one of Actions, Reducer, Saga, or Types.
    1. An exclusion goes to OAuth2 where the names StateProvider and oauth2ImplicitFlow are also allowed
  2. All React Components have the .tsx extension
  3. All non-React Component TypeScript files have the .ts extension
  4. All React Components should be PascalCased, for example NewUserForm
    1. An exclusion goes to index.tsx (which is the app's entry file)
  5. All TypeScript files (non-React Components) should be camelCased (a.k.a. Dromedary Case)
    1. Exclusions go to WindowLocationService as service class
  6. CSS Modules have the naming scheme of [ComponentName].module.scss, for example Project.module.scss

Redux Naming Structure

State Types

  • using Readonly<> for state

ActionTypes Enum

  • HANDLE_ prefix
  • @@subsection prefix
  • GET/DELETE/POST etc. after HANDLE_
  • SUCCESS postfix for storing to state
  • Exception to oauth2

Action function names

  • camelCase name dupe of actiontype

Saga function names

  • camelCase name dupe of actiontype minus handle_

Action Handling

  • useEffect
  • bindActionCreators

Reducer Handling

  • createReducer
  • spread syntax

Saga Handling

  • When Saga's are called
  • using of takeEvery
  • typing of actions in saga (when need to access payload)
  • typing of actions in component (what goes into mapDispatchToProps)

Component Styling

We strive to follow the Sass Guidelines, found at: https://sass-guidelin.es/

Colors

All colours used throughout the application are set in the src/styles/variables/_colors.scss. When applying colours in other stylesheets, always refer to a colour defined in this colour stylesheet file. The name should start with an underscore so it is entirely skipped in the compilation of the SCSS and only those parts that are used in other files are included.

Vars

The vars file holds all variables used throughout the applications. It is split into a few subsections defined by // comments. The vars file is located at src/styles/variables/_vars.scss. The name should start with an underscore so it is entirely skipped in the compilation of the SCSS and only those parts that are used in other files are included.

Placeholders

Placeholders allow us to define re-usable styles without variables that can be used throughout our stylesheets. Whenever you do need variables, always use mixins instead.

Placeholders (and mixins) make it easy to avoid using non-semantic classes (like .class-normalFont), and to distribute collections of styles in libraries. To use a placeholder, @extend the placeholder in the style class, for example:

.class {
  @extend %placeholderName
}
Mixins

Mixins allow us to define re-usable styles with variables that can be used throughout our stylesheet. Whenever you do not need variables, always use placeholders instead.

Mixins (and placeholders) make it easy to avoid using non-semantic classes (like .class-normalFont), and to distribute collections of styles in libraries. To use a mixin, @include the mixin in the style class, for example:

.class {
  @include mixinName
}
Constants

Constants that can be applied throughout the entire SCSS code for reusability, for example the font-family or font-size

Default Overwrites

Overwrite defaults defined in external libraries here, for example defaults used by react-redux-toastr. The code in the library is defined with the SCSS syntax of !default, which means it is assumed the SCSS to use unless it is overwritten in our library-using code.

SCSS Modules

CSS Modules have the naming scheme of [ComponentName].module.scss and can be found in the src/styles/modules folder. Each React Component that needs styling applied should have its own CSS Module. Exceptions are made for:

  1. Styles that are shared between React Components. In this case the these styles should be placed inside the Common.module.scss that is found in the same directory as the other CSS modules.
  2. Styles that have to do with Polyfills are found in polyfills.module.scss.

To start using a module inside a React Component, first import it by using import css from 'styles/modules/[ComponentName].module.scss';.

SCSS Modules should look like:

@import '../variables/colors';
@import '../variables/mixins';

.classStyle {
  background-color: $very-light-grey;
  
  &NestingStyle {
    font-size: 0.8rem;
  }

  &NestingStyle > element {
    color: $blue;          
  } 
}

%placeholderClass {
    height: 1rem;
    width: 100vw;          
}

.classStyleExtending {
  @extend %placeholderClass;        
}

.classIncludeMixin {
  @include someMixin
}

Standardization

SCSS Syntax

  • Placeholders over Mixins unles u
  • Something about moving to webcomponents