DEV Community

Stefano Magni
Stefano Magni

Posted on • Edited on

RouteManager UI coding patterns: Redux Saga

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)

Prefer linearity over side effects

Sagas triggered as a result of a side effect are hard to follow and lead to a lot of unwanted effects.

// ❌ don't
function* foo() {
  yield put({
    type: 'baz',
  })
}
function* bar() {
  yield put({
    type: 'baz',
  })
}
function* baz() {
  while (true) {
    yield take(takeBazAction)
    doBaz()
  }
}
// ✅ do
function* foo() {
  doBaz()
}
function* bar() {
  doBaz()
}
Enter fullscreen mode Exit fullscreen mode

Use call only to allow mocking the called function

Pure/simple functions that do not trigger side effects or read data from external sources should not be call'ed, since controlling them from the tests require useless boilerplate.

// ❌ don't
function* foo(clientId: string) {
  const serverId = yield call(createServerId, clientId)
  const serverEntity = yield call(requestEntity, serverId)
  return yield call(createClientEntity, serverEntity)
}
// ✅ do
function* foo(clientId: string) {
  const serverId = createServerId(clientId)
  const serverEntity = yield call(requestEntity, serverId)
  return createClientEntity(serverEntity)
}
Enter fullscreen mode Exit fullscreen mode

Create frontend-oriented requests

Requests should be frontend-oriented, even when the backend uses different names or a single API for multiple purposes.

// ❌ don't
function* geocode(address: string) {
  yield call(geocodeAddresses, address)
}
function* reverseGeocode(latLng: LatLng) {
  yield call(geocodeAddresses, latLng)
}
function* geocodeAddresses(param: string | LatLng) {
  yield call(executeRequest, 'Geocoding.geocodeAddresses', param)
}
// ✅ do
function* geocode(address: string) {
  yield call(geocodeRequest, address)
}
function* reverseGeocodeRequest(latLng: LatLng) {
  yield call(geocodeAddresses, latLng)
}
function* geocodeRequest(address: string) {
  yield call(executeRequest, 'Geocoding.geocodeAddresses', address)
}
function* reverseGeocodeRequest(latLng: LatLng) {
  yield call(executeRequest, 'Geocoding.geocodeAddresses', latLng)
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)