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:
Small - “Your component should be small”
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
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.
Does one thing - “Your component should do one thing”
Your components should have only one main responsibility: one reason to change.
Split your UI into tiny chunks that each handle one thing.
One level of abstraction - “Your component should have one level of abstraction”
Less than three arguments - “Your component should have only a few arguments (props)”
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
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.
Descriptive name - “Your component should have a descriptive name”
“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.