React Component Design Tips & Tricks

A list of resources for writing good, understandable, and reusable React components for Paragon.


React component design

 

How to write great React - Scott Domes

Highlights

Scott Domes encourages us to always conceptualize our components as functions, even if they are written as class components. Then addressing the question, “what makes a good function?” the article walks through the five factors outlined in Robert Martin’s Clean Code as they apply to React component design. The five factors and some quotes:

  1. Small - “Your component should be small”

    1. The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. — Clean Code

    2. 50 lines is a good rule of thumb for the body of your component (for class components, that is the render method). If looking at the total lines of the file is easier, most component files should not exceed 250 lines. Under 100 is ideal.

  2. Does one thing - “Your component should do one thing”

    1. Your components should have only one main responsibility: one reason to change.

    2. Split your UI into tiny chunks that each handle one thing.

  3. One level of abstraction - “Your component should have one level of abstraction”

  4. Less than three arguments - “Your component should have only a few arguments (props)”

    1. The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.. — Clean Code

    2. Here’s a more relaxed rule of thumb. Three props is fine. Five props is a code smell. More than seven props is a crisis.

  5. Descriptive name - “Your component should have a descriptive name”

    1. “Having a hard time naming your component is a sign it’s doing too much. The answer to ‘what does this component do?’ should be simple, and lend itself to a descriptive name.”

Prop-Types

All component properties should be annotated with associated prop-types from the react prop-types library.
https://reactjs.org/docs/typechecking-with-proptypes.html

React app design

Thinking in React - reactjs.org

“One of the many great parts of React is how it makes you think about apps as you build them. In this document, we’ll walk you through the thought process of building a searchable product data table using React.”

  • Start With A Mock

  • Step 1: Break The UI Into A Component Hierarchy

  • Step 2: Build A Static Version in React (no state)

  • Step 3: Identify The Minimal (but complete) Representation Of UI State

  • Step 4: Identify Where Your State Should Live

  • Step 5: Add Inverse Data Flow


Remember that React components are just Javascript

Don’t let JSX syntax fool you. The <Component /> jsx syntax is transpiled into React.createElement(Component, props, ...children). Example from the React docs:

 

With JSX

class Hello extends React.Component { render() { return <div>Hello {this.props.toWhat}</div>; } } ReactDOM.render( <Hello toWhat="World" />, document.getElementById('root') );

No JSX

class Hello extends React.Component { render() { return React.createElement('div', null, `Hello ${this.props.toWhat}`); } } ReactDOM.render( React.createElement(Hello, {toWhat: 'World'}, null), document.getElementById('root') );

 

 

Consider writing a React component as a function. Writing a good component shares a lot in common with writing a good function. Robert Martin’s Clean Code (also wow) outlines some good rules of thumb for functions:

  • Small!

  • Do One Thing

  • One Level of Abstraction per Function

  • Use Descriptive Names.

Keep your component small

“Functions should hardly ever be 20 lines long.” Less than 50 lines is a good rule of thumb for the body of your component (for class components, that is the render method).

Do One Thing

Aggressively split apart your components to reduce the number of jobs they have. Good related article.

Aim for fewer levels of abstraction

todo

Move lists into separate components

// Don't write loops in a component with other jobs const CourseResults = ({resultCount, courses}) => ( <div> <h1>Results: {resultCount}</h1> <Filters /> { courses.map(course => <CourseCard course={course} />) } <Pagination /> </div> ); // Move it into a list component const CourseResults = ({resultCount, courses}) => ( <div> <h1>Results: {resultCount}</h1> <Filters /> <CourseCardList courses={courses} /> <Pagination /> </div> );

Avoid nesting render functions


 

Use as few props (function parameters) as possible

“The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic).”

 

Avoid flag arguments where possible (Related source)

Consider enumerating variants or state, or creating discrete components:

 

Good, but what if your buttons would vary significantly? (Maybe “danger” always gets a stop icon)

Clearest, though requires abstracting common behavior and definitions to a base button component or object.

 

Leverage a router over conditionals where possible

Nope

 

Meh

Create two components, don’t make the Form component do two things.

Yup

No booleans needed!

 

Use objects as props for logical sets of data

“When a function seems to need more than two or three arguments it is likely that some of those arguments ought to be wrapped into a class of their own”

 

Benefits:

  • If the user object has new properties added, this component doesn’t need a refactor

  • It’s clear what the concept of a “user” is.

 


Use Descriptive Names

“Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.”

The names of props should reflect their prop type (Source)

  • Booleans

    • is (isVisible, isActive)

    • has (hasCancelButton, hasIcon)

    • can (canToggle, canSubmit)

  • Number

    • num__ (numItems, numRows)

    • __Count (itemCount, rowCount)

    • __Index (itemIndex, rowIndex)

  • Array

    • Use plural nouns (rows, items, users)

  • Object

    • Use the appropriate noun (item, user)

  • Node

    • __Node (headerNode, titleNode)

  • Element

    • __Element (headerElement, titleElement)

Name components and their props to describe the component itself. (Source).

Describe what your component is or does, not why. It helps to ask yourself, “If I used this component in a totally different context, would these names make sense?”

 

Event handler props

  • Use the on__ prefix for event handler props.

  • Use handle prefix for event handlers that get passed in (generally)

<Button onClick={handleClick} />

<UserItem user={user} onSelect={handleUserSelect} />

Techniques to maintain flexibility while still doing a job

  • Render Props

  • sending in the type

Use React Context for interoperable components that need to appear in many places in the component hierarchy.

Example A/B test data.