Skip to content

Instantly share code, notes, and snippets.

@jeremyckahn
Last active July 5, 2018 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeremyckahn/e72dcd9300df5a5b934976fa60cbbc70 to your computer and use it in GitHub Desktop.
Save jeremyckahn/e72dcd9300df5a5b934976fa60cbbc70 to your computer and use it in GitHub Desktop.
Getting weird with functions

A Reductive Approach to React Components

What follows is the result of Mad Science™ in React development, and is either a great idea or a terrible one. The jury is still out, and I will leave it up to the community to decide whether this is pragmatic or heresy.

I am a huge fan of React. I love the functional approach to app development and the parallels I see between the functional reactive programming model and game design. The established best practices largely make sense to me, particularly the parts about lifting state up to higher level-components. Anecdotally, I have found that lifting state all the way up to the highest level in a component hierarchy yields some really valuable benefits (which I have covered elsewhere). This document is predicated upon one of the side benefits of that approach: When all of your state lives in a single component at the top of the tree, everything below it can remain a dumb, functional component:

const List = ({ arr }) => (
  <ul>
    {arr.map(
      el => <li>{el}</li>
    )}
  </ul>
);

Notice that this component uses the concise body ES6 arrow function syntax. I really love this minimalistic style for components, not only for its aesthetic expressiveness, but also because of the constraints it places on what you can do with this component. For example, you can't have particularly complex logic in a function like this before it gets completely out of hand. Once that happens, it probably makes sense to break the logic out into a helper component. But that's true anyways; the limitations of the syntax just help to nudge you in that direction:

const ListItem = ({ content }) => (
  <li>
    {content}
  </li>
);

const List = ({ arr }) => (
  <ul>
    {arr.map(el => <ListItem content={el} />)}
  </ul>
);

There are many well-documented benefits to breaking out large components into smaller ones, not least of which being code reuse. This is all nice and clean for contrived List examples, but what about in the Real World™ where components are necessarily complex and can't be cleanly implemented in an arrow function alone, even with refactoring?

That's where ES6 default parameters are handy! As I've explored previously, you can execute arbitrarily complex logic in default parameters:

const List = ({
  arr,
  sum = arr.reduce((acc, el) => acc + el, 0)
}) => (
  <ul>
    <li>The total for the items below are {sum}</li>
    {arr.map(
      el => <li>{el}</li>
    )}
    <li>The total for the items above are {sum}</li>
  </ul>
);

This is admittedly a bit simplistic, but it illustrates the fact that arr could be processed into sum once and used multiple times, without the overhead of calling arr.reduce again. Indeed, there's nothing here that couldn't be achieved with multiple expressions inside the function body:

const List = ({ arr }) => {
  const sum = arr.reduce((acc, el) => acc + el, 0);

  return (<ul>
    <li>The total for the items below are {sum}</li>
    {arr.map(
      el => <li>{el}</li>
    )}
    <li>The total for the items above are {sum}</li>
  </ul>);
};

However, the prior example looks a bit cleaner to me, and the constraints of a concise body function nudge me towards more expressive code. They also force me to put my more complex rendering logic at the top of the function, leaving the body of the function to be expressly focused on the markup.

But does it scale??!?

I think so! I have used this approach to pretty great effect in two projects so far (Merkaba and rekapi-timeline), at least enough to keep going with it. I am finding the constraints to be more helpful than limiting. I admit that it's a largely subjective improvement, but perhaps you will also benefit from writing your non-root React components as dumb, concise body functions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment