Forem

Stefano Magni
Stefano Magni

Posted on • Edited on

3 2

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

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay