Ankurito Codes Live!

Ankurito Codes Live!

Using flow with chainable methods in React components

Using flow with chainable methods in React components

Series: "Passing data down to the pipe"

I want to tell the story of a navigation menu. It is what you'd expect: a bunch of links displayed in HTML. But while your first instinct may be to jump quickly to the task of writing the links by hand, the approach here illustrated is the "data first" approach. I mean, I define my data right away, and it doesn't matter if it's in the component, in the component's folder, or in a /data folder. The point is, we are using data.

const labels = {
  "links": {
    "home": "Home",
    "about": "About",
    "help": "Help"
  }
}

const links = [
  {
    id: "home",
    route: "/home"
  },
  {
    id: "about",
    route: "/about"
  },
  {
    id: "help",
    route: "/help"
  }
];

So, there's nothing interesting about this mock data other than it's normalised. You can already see that we will be able to find the label of a particular link by accessing its id in the labels object. So let's write that code down.

const getLabel = (id) => labels.links[id];

const addLabel = (link) => ({
  ...link,
  label: getLabel(link.id)
});

const menuLinks = map(addLabel);

// menuLinks = [  { id: "help", route: "/help", label: "Help" }, ... ]

menuLinks is an array with objects that have the information we need to render the list in JSX. The objects look like this:

  {
    id: "help",
    route: "/help",
    label: "Help"
  }

But hold up. The designers have updated the task with some additional menu items. Some of these are sub menus that we need to implement (when you hover over a menu that has a sub menu, the sub menu should be visible.) We don't want the sub menus to show up at all when the component first loads, so let's add yet another lodash function to the mix to filter out the sub menu items, called filter.

const onlyTopLevel = (link) => link.route && !link.parent;

const filteredLinks = filter(onlyTopLevel);

const filteredMenuLinks = menuLinks(filteredLinks(links))

A couple of things not-quiet-right with the code as it is now. One, there are variable creating, adding more things we have to keep track of. So far, we've got filteredMenuLinks, filteredLinks, onlyTopLevel, on and on and on. And two, the way we are calling these two functions that take one's return value as the argument of another is awkward and hard to follow, even after just two functions: menuLinks(filteredLinks(links)). To fix this, we can use flow.

const list = flow(filter(onlyTopLevel), map(addLabel))(links);

We've saved two variables, but more importantly, the code isn't any harder to read. You flow data (links) through two functions that both accept and return collections. This is important. Both functions in flow must be able to return and accept things in an order such that it makes sense. You don't want to give a function that excepts a single Items a whole array of items.

Photo by sum+it from Pexels

 
Share this