Declarative JavaScript
Tags: javascript, react, functional
Published: 2023-10-23 16:19:33

---
title: Declarative JavaScript

published: true
description: React
tags: javascript, react, fp,
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuqu4f185kwlfxer1aqo.png
---

Dipping your toes into the world of functional programming can sometimes feel overwhelming. The transition from understanding its core concepts to actually applying them in real-world scenarios can be challenging. If you're familiar with some functional programming techniques but unsure when and how to use them, you're in the right place. In this article, we'll discuss some key concepts and their practical uses, and also explore how declarative programming is connected to functional programming.


> Imagine writing JavaScript code without using variables, loops, or logical constructs. 

This might sound bold, right? But how often do you find yourself writing code like this to toggle the visibility of a component in JSX?

```jsx
<div>
  {hasComments ? <Comments/> : null}
</div>
```

We can rewrite it in a more declarative way:

```jsx
<div>
  <Comments visible={hasComments}/>
</div>
```

As noted, this version is *more declarative*. Although we still use the conditional statement `if` internally, the goal is to abstract it from the main code.

```jsx
const Comments = ({visible}) => {
  if (!visible) return null
  ...
}
```

The `visible` property prompts us to write code in a declarative manner, even though the component itself is partially implemented in an imperative style.

Let's take a look at the logical operator `switch`, which isn't just closely related to `if`, but is also a favorite among many developers:

```jsx
const App = () => {
  const role = useUserRole()
  let Component
  switch(role) {
    case 'ADMIN': {
      Component = AdminView
      break
    }
    case 'EDITOR': {
      Component = EditorView
      break
    }
    case 'USER': {
      Component = UserView
      break
    }
    default: {
      Component = GuestView
      break
    }
  }

  return (
    <main>
      <NavBar/>
      <Component/>
    </main>
  )
}
```

Perhaps you’ve already guessed where I'm heading with this, especially since a similar approach is implemented in React Router.

```jsx
const App = () => (
  <main>
    <NavBar/>
    <Switch test={useUserRole()}>
      <Case when='ADMIN' use={AdminView}/>
      <Case when='EDITOR' use={EditorView}/>
      <Case when='USER' use={UserView}/>
      <Otherwise use={GuestView}/>
    </Switch>
  </main>
)
```

Let’s consider this code from the perspective of functional programming, where HTML/JSX tags are treated as functions that produce HTML output and tag attributes act as function input parameters.

```jsx
// it will return HTML: <main id="app">Hello World<main/>
main({id: 'app', children: 'Hello world!'})

// The second parameter can be used as a children attribute
main({id: 'app'}, 'Hello world!')
```

Drawing from the concept above, let's express the JSX code through function sets. It's pertinent to note that `switch/case` are reserved JavaScript keywords, so we'll add underscores:

```jsx
const app = () => (
  main(
    navbar(),
    switch_({test: useUserRole()},
      case_({when: 'ADMIN', use: AdminView}),
      case_({when: 'EDITOR', use: EditorView}),
      case_({when: 'USER', use: UserView}),
      otherwise({use: GuestView}),
    )
  )
)
```

As you can see, declarative code doesn't always mean HTML or JSX. In JavaScript, we can use functions to represent the syntax of these languages. According to JS conventions, functions should represent actions and usually start with a verb, such as `find` or `setTitle`. However, in this case, functions can also represent an entity. For example, an SQL query can be written like this:

```jsx
// a function composition
query(
  select('name', 'email', 'country'),
  from('users'),
  where({age: less(21)}),
  groupBy('country'),
)

// or as a chaining function like Promise
select('name', 'email', 'country')
  .from('users')
  .where({age: less(21)})
  .groupBy('country')

// SELECT name, email, country FROM users WHERE age < 21 GROUP BY country
```

This way, I want to draw a connection between declarative and functional programming. I believe that functional programming does not intentionally pursue declarativeness; rather, it is a result of extensively using functions. It's worth mentioning that functional programming is not solely about the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle, which involves extracting repetitive code into separate functions. At its core, functional programming focuses on crafting universal functions that are both highly composable and versatile in application.

Now we can take another look at the `switch/case` and represent them as a plain function:

```jsx
const selectComponent= ({test, cases, defaultValue}) => {
  const found = cases.find([value] => test === value)
  return found?.at(1) || defaultValue
}

const App = () => {
  const role = useUserRole()
  const Component = selectComponent({
    test: role,
    cases: [
      ['ADMIN', AdminView],
      ['EDITOR', EditorView],
      ['USER', UserView],
    ],
    defaultValue: GuestView,
  })

  return (
    <main>
      <NavBar/>
      <Component/>
    </main>
  )
}
```

The current implementation falls short in terms of flexibility and composability with other functions. To enhance its utility, we could transform the `selectComponent` function into a higher-order function and break it down into more granular functions.

```jsx
const select = (...fns) => value => fns.reduce(
  (found, fn) => found || fn(value), null
)

const when = (test, wanted) => value => {
  const matched = typeof test === 'function' ? test(value) : test === value
  return matched && wanted
}

const selectComponent = select(
  when('ADMIN', AdminView),
  when('EDITOR', EditorView),
  when('USER', UserView),
  () => GuestView,
)

const Component = selectComponent('EDITOR') // -> EditorView
```

If you're only somewhat familiar with the concept of curried functions, this serves as a solid illustration of their practical use. While a curried function resembles a standard function, its execution can be postponed until all its parameters have been supplied. This capability not only facilitates function composition, but also renders the code more declarative and understandable.

We have the capability to craft various iterations of the `when` function, all the while ensuring that the primary `select` function remains intact and requires no alterations. Below is an example that demonstrates how to support lazy loading of components:

```jsx
import {Suspense, lazy} from 'react'

const when = (test, path) => value => (
  test === value && lazy(() => import(path))
)

const selectComponent = select(
  when('ADMIN', './admin-view'),
  when('EDITOR', './editor-view'),
  when('USER', './user-view'),
  () => GuestView,
)

const App = () => {
  const role = useUserRole()
  const Component = selectComponent(role)

  return (
    <main>
      <NavBar/>
      <Suspense fallback={<div>Loading...</div>}>
        <Component/>
      </Suspense>
    </main>
  )
}
```

If we take a much closer look at the initial version of the `selectComponent` function, it becomes clear that it is not possible to extend its functionality without making changes. With this approach, we can break down the code into smaller functions, each with a specific task in mind. This improves the test coverage experience and minimizes the future changes impacting multiple sections of the code. If we ever need to expand the functionality, we can simply add a new function and integrate it with the existing functions rather than rewriting the main function each time.

```jsx
const between = (min, max) => n => (
  min >= n && n <= max
)

// range of values
const toGrade = select(
  when(val => val > 90, 'A'),
  when(between(80, 89), 'B'),
  when(between(70, 79), 'C'),
  when(between(50, 69), 'D'),
  () => 'F',
)

const grade = toGrade(81) // -> 'B'
```

In [the next post](https://dev.to/sultan99/simplified-redux-fp) we’ll take a detailed look at this technique using the example of creating Redux reducers. We will learn how to replace the `try/catch` approach and continue the discussion on functional programming.

```jsx
const authReducer = createReducer(
  initialState,
  on('SIGN_IN', signIn),
  on('SIGN_OUT', signOut),
  on('SIGN_OUT', clearCookies),
)
```