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
.
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;
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 throwError
as part of normal, common execution flow (should be rare case, i.e.tryGetCacheValue
which throwsError
instead of returningundefined
). If getter is expected to return value in typical scenario, then use regularget
prefix and specifyundefined
as one of return types. - Use
orUndefined
for cases where there are multiple getters with/withoutundefined
as return type. - Use
orIgnore
for setters which don't throwError
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'
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).