DEV Community

Stefano Magni
Stefano Magni

Posted on

RouteManager UI coding patterns: Generic ones

This is an non-exhaustive list of the coding patterns the WorkWave RouteManager's front-end team follows. The patterns are based on years of experience writing, debugging, and refactoring front-end applications with React and TypeScript but evolves constantly. Most of the possible improvements and the code smells are detected during the code reviews and the pair programming sessions.

(last update: 2021, July)

Use named functions for components and utilities

Named functions ease debugging, both in the browser devTools and in the React devTools.

// ❌ don't
export const getFoo = () => {}
export const useFoo = () => {}
export const FooComponent = () => {}

// ✅ do
export function getFoo() {}
export function useFoo() {}
export function FooComponent() {}
Enter fullscreen mode Exit fullscreen mode

Anyway, avoid them for inline functions.

// ❌ don't
useEffect(function myEffect() {})
addEventListener(function listener() {})

// ✅ do
useEffect(() => {})
addEventListener(() => {})
Enter fullscreen mode Exit fullscreen mode

Prefer self-explicative code instead of comments

Documentation and comments are helpful, but you can reduce them with self-explanatory code.

// ❌ don't
/**
 * Filter out the non-completed orders.
 */
function util(array: Order[]) {
  const ok: Order[] = []

  for(const item of array) {
    const bool = item.status === 'completed'
    if(bool) {
      ok.push(item)
    }
  }

  return ok
}

// ✅ do
function filterOutUncompletedOrders(orders: Order[]) {
  const completedOrders: Order[] = []

  for(const order of orders) {
    if(order.status === 'completed') {
      completedOrders.push(order)
    }
  }

  return completedOrders
}
Enter fullscreen mode Exit fullscreen mode

Code must be simple (KISS principle)

Always prefer readability over smartness, the future-reader will thank you.

// ❌ don't
function getLabel(cosmetic?: CardCosmetic, isToday?: boolean): string {
  const pieces: string[] = []
  if (isToday) {
    pieces.push('today')
  }

  if (cosmetic === 'edge-of-selection' || cosmetic === 'selected') {
    pieces.push('selected')
  }

  return pieces.join(' ')
}

// ✅ do
function getLabel(cosmetic?: CardCosmetic, isToday?: boolean): string {
  const selected = cosmetic === 'edge-of-selection' || cosmetic === 'selected'

  if (isToday && selected) return 'today selected'
  if (selected) return 'selected'
  if (isToday) return 'today'

  return ''
}
Enter fullscreen mode Exit fullscreen mode

Never spread function's arguments

Spreading functions' arguments prevent debugging the source object, avoid it.

// ❌ don't
function foo({ bar, baz }) {
  /* ... rest of the code... */
}

// ✅ do
function foo(params) {
  const { bar, baz } = params
  /* ... rest of the code... */
}
Enter fullscreen mode Exit fullscreen mode

Prefer string unions over booleans

String unions prevent boolean states to explode and inconsistencies.

// ❌ don't
type Status = {
  idle: boolean
  loading: boolean
  complete: boolean
  error: boolean
}

// ✅ do
type Status = 'idle' | 'loading' | 'complete' | 'error'
Enter fullscreen mode Exit fullscreen mode

Prefer positive conditions

Positive conditions avoid the reader's mind to reverse the variable name meaning.

// ❌ don't
const allowViewers = false
if(userType === 'viewer' && !allowViewers) {
  // ...
}

// ✅ do
const blockViewers = true
if(userType === 'viewer' && blockViewers) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Bailouts

Keep pre-checks and bailouts on a single line, without braces. Reducing the function's height improves readability.

// ❌ don't
function foo() {
  if(/* condition 1 */) {
    return
  }
  if(/* condition 2 */) {
    return
  }
  if(/* condition 3 */) {
    return
  }
}

// ✅ do
function foo() {
  if(/* condition 1 */) return
  if(/* condition 2 */) return
  if(/* condition 3 */) return

  /* ... rest of the code... */
}
Enter fullscreen mode Exit fullscreen mode

Avoid fake bailouts

If a function returns some possible valid values, don't confuse the reader by treating one of them as a "default" value and the other ones as bailouts or early-returns.

// ❌ don't
function getMainColor(theme: 'light' | 'dark') {
  if(theme === 'dark') {
    return 'black'
  }

  return 'white'
}

// ✅ do
function getMainColor(theme: 'light' | 'dark') {
  if(theme === 'dark') {
    return 'black'
  } else {
    return 'white'
  }
}

// or

function getMainColor(theme: 'light' | 'dark') {
  switch(theme) {
    case 'light':
      return 'white'
    case 'dark':
      return 'black'
  }
}
Enter fullscreen mode Exit fullscreen mode

Logical nullish assignment

Nullish Coalescing assignment can be compressed by using the ??= operator.

// ❌ don't
obj.foo = obj.foo ?? {}

// ✅ do
obj.foo ??= {}
Enter fullscreen mode Exit fullscreen mode

Prefix higher-order functions with create

Higher-order functions (functions that create other pre-configured functions) must be prefixed with "create".

// ❌ don't
function getLogger(tag: string) {
  return function logger(message: string) {
    console.log(tag, message)
  }
}

// ✅ do
function createLogger(tag: string) {
  return function logger(message: string) {
    console.log(tag, message)
  }
}
Enter fullscreen mode Exit fullscreen mode

Avoid default exports

Default exports force the consumer to give a name to the imported module, removing control from the module itself. Always prefer named exports.

// ❌ don't
const foo = 'bar'
export default foo

// ✅ do
export const foo = 'bar'
Enter fullscreen mode Exit fullscreen mode

Prefer async code for promises

Async code is more condensed and easier to read, even for non Promise-experts.

// ❌ don't
function load() {
  return service.then(() => {
    /* ... rest of the code... */
  }).cetch(() => {
    /* ... rest of the code... */
  })
}

// ✅ do
function async load() {
  try {
    await service()
    /* ... rest of the code... */
  } catch {
    /* ... rest of the code... */
  }
}
Enter fullscreen mode Exit fullscreen mode

Pass only the needed arguments

Functions must be passed only with the used arguments.

// ❌ don't
function isComplete(order: Order) {
  return order.status === 'complete'
}

// ✅ do
function isComplete(status: OrderStatus) {
  return status === 'complete'
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)