RouteManager UI coding patterns: Immutability

noriste

Stefano Magni

Posted on July 20, 2021

RouteManager UI coding patterns: Immutability

This is a 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.

(please note: I do not work for WorkWave anymore, these patterns will not be updated)

(last update: 2022, March)

We guarantee objects' immutability through ESLint. We use the no-param-reassign rule with the following configuration

'no-param-reassign': [
  'error',
  {
    props: true,
    ignorePropertyModificationsForRegex: [
      // standard array.reduce
      'acc',
      // array.reduce with params named differently from `acc`. They must end with `Acc` at least
      'Acc$',
      // standard Immer draft
      'draft',
      // Immer drafts
      'Draft$',
      // refs passed around hooks
      'Ref$',
      // parameters prefixed by `mutable`
      '^mutable',
    ],
  },
],
Enter fullscreen mode Exit fullscreen mode

Please note that enabling mutability through variable names helps the reader only if the whole chain of callers uses the prefix/suffix.

// ❌ don't
function a(array: string[]) {
  b(array)
}

function b(array: string[]) {
  c(array)
}

function c(mutableArray: string[]) {
  mutableArray.splice(1)
}

// ✅ do
function a(mutableArray: string[]) {
  b(mutableArray)
}

function b(mutableArray: string[]) {
  c(mutableArray)
}

function c(mutableArray: string[]) {
  mutableArray.splice(1)
}
Enter fullscreen mode Exit fullscreen mode

Accumulators

Array.reduce accumulators can be mutated, if named acc or suffixed by Acc.

// ❌ don't
[1, 2, 3].reduce<number>((map, item) => {
  return map[item] = item
}, 0)

// ✅ do
[1, 2, 3].reduce<number>((acc, item) => {
  return acc[item] = item
}, 0)

[1, 2, 3].reduce<number>((mapAcc, item) => {
  return mapAcc[item] = item
}, 0)
Enter fullscreen mode Exit fullscreen mode

Immer' drafts

Immer' drafts can be mutated, if named draft or suffixed by draft.

// ❌ don't
const next = produce(prev, temp => {
  temp.message = message
})

// ✅ do
const next = produce(prev, draft => {
  draft.message = message
})

const next = produce(prev, emailDraft => {
  emailDraft.message = message
})
Enter fullscreen mode Exit fullscreen mode

React's refs

React's refs can be mutated, if suffixed by Ref.

// ❌ don't
export function useNow(now) {
  useEffect(() => {
    now.current = Date.now()
  }, [])
}

export function useNow(ref) {
  useEffect(() => {
    ref.current = Date.now()
  }, [])
}

// ✅ do
export function useNow(nowRef) {
  useEffect(() => {
    nowRef.current = Date.now()
  }, [])
}
Enter fullscreen mode Exit fullscreen mode

Mutable arguments

The arguments of a function can be mutated, if prefixed by mutable.

// ❌ don't
export function setToday(dates) {
  dates.today = new Date()
}

// ✅ do
export function setToday(mutableDates) {
  mutableDates.today = new Date()
}
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
noriste
Stefano Magni

Posted on July 20, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024

Modern C++ for LeetCode 🧑‍💻🚀
leetcode Modern C++ for LeetCode 🧑‍💻🚀

November 29, 2024