RouteManager UI coding patterns: Immutability
Published: 2021-07-20 06:34:00


> This is a non-exhaustive list of the coding patterns the [WorkWave RouteManager's](https://www.workwave.com/route-manager/) **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](https://eslint.org/docs/rules/no-param-reassign) rule with the following configuration
```js
'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',
    ],
  },
],
```

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

```ts
// ❌ 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)
}
```

- [Accumulators](#accumulators)
- [Immer' drafts](#immer-drafts)
- [React's refs](#reacts-refs)
- [Mutable arguments](#mutable-arguments)


## Accumulators

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

```ts
// ❌ 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)
```

## Immer' drafts

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

```ts
// ❌ 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
})
```

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

```ts
// ❌ 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()
  }, [])
}
```


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

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

// ✅ do
export function setToday(mutableDates) {
  mutableDates.today = new Date()
}
```