Skip to main content
Version: ACE 5

ACE coding style guide

These are the rules and best practices that will be checked for ACE Designer in the pull requests.

React

Use React.FC interface for components

Use React Functional components via React.FC interface, rather than Class components.

Motivation

Using React.FC interface for components guides the developer to return the proper React FunctionalComponent structure.

While type inference works great, unless components built are actually used, this will trigger no warnings. It is also convenient to see warnings during component development, that returned structure is not correct, rather than to discover type error when writing the unit test.

Possible issue that is detected during compile/runtime

// no type check error
export const Example = () => "<></>"

Issue detected early on while constructing the component

// tsc detects, that returned type cannot be used by React
export const Example: React.FC = () => "<></>"

// as intended
export const Example: React.FC = () => <></>

Action handlers in React component props

Motivation

Code readability is better, when everything that is related to the particular function is located in the Component module.

When to pass action handlers in props

If component is reusable (for example FlowListItem).

When to create action handlers in module

If component is not reusable (for example FlowList).

React hooks

Use React Hooks to manage component state, apply effects.

Theme in UI Components

Use UI Theme colours, fonts, grid sizes for styling, rather than define them inline in the component style.

Redux

Redux hooks

Use Redux hooks to manipulate application state and retrieve information from application state.

Thunk error handling

await dispatched actions and handle response (either by checking if action is rejected or .unwrap() to throw exceptions.

If dispatched action is intended to be fire and forget, then need to add comment to indicate that.

Cyclic dependency problem

In the scenario that the file defining the slice is importing an action, and the file defining the action is also importing an action from the file defining the slice, a cyclic dependency occurs. Cyclic dependencies currently break jest tests.

How to resolve cyclic dependencies

To resolve the cyclic dependency, the action coming from the slice has to be extracted. This can be done by manually creating an action outside the file containing the slice by using createAction from @reduxjs/toolkit.

reducers.ts (extracting the action)
import { createSlice } from "@reduxjs/toolkit";
import {
myExtraAction,
+ action1
} from "./actions";

const mySlice = createSlice({
name: "slice",
initialState: {},
reducers: {
- action1: (state) => { state.flag1 = true; },
action2: (state) => { state.flag2 = true; }
},
extraReducers: (builder) => {
builder
.addCase(myExtraAction, (state, action) => {
state.flag2 = state.flag1;
})
+ .addCase(action1, (state, action) => {
+ state.flag1 = true;
+ })
},
});

export default settingsSlice.reducer;
export const {
- action1,
action2,
} = slice.actions;
actions.ts (extracting the action)
import { 
+ createAction,
createAsyncThunk
} from "@reduxjs/toolkit";

-import {
- action1
-} from "./reducers"

import {
myService
} from "../services"

+const action1 = createAction<{ id: string }>("slice/action1");

export const myExtraAction = createAsyncThunk(
"slice/myExtraAction",
async (_, { dispatch, getState }) => {
await myService();
dispatch(action1());
}
);

Cypress

Waiting till element is found

Don't unnecessary wait in tests.

Note - though cypress typically waits till element exists chainable commands may not work.

cy.get("some-selector").contains("some text) - in this example Cypress waits till it finds some-selector and then searches some text, but if some-selector is not unique and exists in page, then Cypress fails to wait till some text appears.

One way to fix it is to use cy.contains("some-selector", "some text)

Storybook

Storybook actions

Use Storybook actions to handle events instead of console.log

Motivation

It allows to see actions immediately in Storybook without checking console

Javascript

Arrow functions vs functions

Motivation

This guide explains arrow function use cases and differences.

Performance vise Arrow functions are the same as regular Functions.

Use arrow functions when there is no intent to instantiate the Function with new operator or use Function instance scoped context (this).

When to use Function

If developer wants to leverage the function context (this) and imply that there will be multiple instances of Cache Singletons, that will use different cache instances.

function Cache() {
this.cache = new Map()
this.getCachedValue = (key) => this.cache.get(key)
}

When to use Arrow Function

If developer knows that there will be only one cache per module/solution.

const cache = new Map()
const getCachedValue = (key: string) => {
return cache.get(key)
}

Naming conventions

Opinionated naming conventions in the project.

File names

  • Folders should be in kebab-case
  • File names should be in camelCase.ts for regular Typescript files.
  • Files which contains only class or type can use PascalCase.ts
  • React components uses PascalCase.tsx as per React convention.
  • React hooks and helper files should use .tsx or .ts extension depending on whether it contains JSX or not

Method prefixes/suffixes

  • Use try prefix only for getters that throw Error as part of normal, common execution flow (should be rare case, i.e. tryGetCacheValue which throws Error instead of returning undefined). If getter is expected to return value in typical scenario, then use regular get prefix and specify undefined as one of return types.
  • Use orUndefined for cases where there are multiple getters with/without undefined as return type.
  • Use orIgnore for setters which don't throw Error if value is not set.

lodash imports

Use specific module import to import lodash methods.

Use

import foo from 'lodash/foo'

Don't use

import { foo } from 'lodash'
//or
import _ from 'lodash'

Motivation

Application layout

  • Generic components must stretch to the size of parent container.
  • Generic components must not have margins and paddings beyond the need of cosmetic look.
  • Application layout must be functional with screen width size of md (900px).