• Stars
    star
    1,724
  • Rank 27,066 (Top 0.6 %)
  • Language
    HTML
  • Created over 8 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Patterns for React Developers

Contents

Stateless function

Stateless functions are a brilliant way to define highly reusable components. They don't hold state; they're just functions.

const Greeting = () => <div>Hi there!</div>

They get passed props and context.

const Greeting = (props, context) =>
  <div style={{color: context.color}}>Hi {props.name}!</div>

They can define local variables, where a function block is used.

const Greeting = (props, context) => {
  const style = {
    fontWeight: "bold",
    color: context.color,
  }

  return <div style={style}>{props.name}</div>
}

But you could get the same result by using other functions.

const getStyle = context => ({
  fontWeight: "bold",
  color: context.color,
})

const Greeting = (props, context) =>
  <div style={getStyle(context)}>{props.name}</div>

They can have defined defaultProps, propTypes and contextTypes.

Greeting.propTypes = {
  name: PropTypes.string.isRequired
}
Greeting.defaultProps = {
  name: "Guest"
}
Greeting.contextTypes = {
  color: PropTypes.string
}

JSX spread attributes

Spread Attributes is a JSX feature. It's syntactic sugar for passing all of an object's properties as JSX attributes.

These two examples are equivalent.

// props written as attributes
<main className="main" role="main">{children}</main>

// props "spread" from object
<main {...{className: "main", role: "main", children}} />

Use this to forward props to underlying components.

const FancyDiv = props =>
  <div className="fancy" {...props} />

Now, I can expect FancyDiv to add the attributes it's concerned with as well as those it's not.

<FancyDiv data-id="my-fancy-div">So Fancy</FancyDiv>

// output: <div class="fancy" data-id="my-fancy-div">So Fancy</div>

Keep in mind that order matters. If props.className is defined, it'll clobber the className defined by FancyDiv

<FancyDiv className="my-fancy-div" />

// output: <div className="my-fancy-div"></div>

We can make FancyDivs className always "win" by placing it after the spread props ({...props}).

// my `className` clobbers your `className`
const FancyDiv = props =>
  <div {...props} className="fancy" />

You should handle these types of props gracefully. In this case, I'll merge the author's props.className with the className needed to style my component.

const FancyDiv = ({ className, ...props }) =>
  <div
    className={["fancy", className].join(' ')}
    {...props}
  />

destructuring arguments

Destructuring assignment is an ES2015 feature. It pairs nicely with props in Stateless Functions.

These examples are equivalent.

const Greeting = props => <div>Hi {props.name}!</div>

const Greeting = ({ name }) => <div>Hi {name}!</div>

The rest parameter syntax (...) allows you to collect all the remaining properties in a new object.

const Greeting = ({ name, ...props }) =>
  <div>Hi {name}!</div>

In turn, this object can use JSX Spread Attributes to forward props to the composed component.

const Greeting = ({ name, ...props }) =>
  <div {...props}>Hi {name}!</div>

Avoid forwarding non-DOM props to composed components. Destructuring makes this very easy because you can create a new props object without component-specific props.

conditional rendering

You can't use regular if/else conditions inside a component definition. The conditional (ternary) operator is your friend.

if

{condition && <span>Rendered when `truthy`</span> }

unless

{condition || <span>Rendered when `falsey`</span> }

if-else (tidy one-liners)

{condition
  ? <span>Rendered when `truthy`</span>
  : <span>Rendered when `falsey`</span>
}

if-else (big blocks)

{condition ? (
  <span>
    Rendered when `truthy`
  </span>
) : (
  <span>
    Rendered when `falsey`
  </span>
)}

Children types

React can render children of many types. In most cases it's either an array or a string.

string

<div>
  Hello World!
</div>

array

<div>
  {["Hello ", <span>World</span>, "!"]}
</div>

Functions may be used as children. However, it requires coordination with the parent component to be useful.

function

<div>
  {(() => { return "hello world!"})()}
</div>

Array as children

Providing an array as children is a very common. It's how lists are drawn in React.

We use map() to create an array of React Elements for every value in the array.

<ul>
  {["first", "second"].map((item) => (
    <li>{item}</li>
  ))}
</ul>

That's equivalent to providing a literal array.

<ul>
  {[
    <li>first</li>,
    <li>second</li>,
  ]}
</ul>

This pattern can be combined with destructuring, JSX Spread Attributes, and other components, for some serious terseness.

<div>
  {arrayOfMessageObjects.map(({ id, ...message }) =>
    <Message key={id} {...message} />
  )}
</div>

Function as children

Using a function as children isn't inherently useful.

<div>{() => { return "hello world!"}()}</div>

However, it can be used in component authoring for some serious power. This technique is commonly referred to as render callbacks.

This is a powerful technique used by libraries like ReactMotion. When applied, rendering logic can be kept in the owner component, instead of being delegated.

See Render callbacks, for more details.

Render callback

Here's a component that uses a Render callback. It's not useful, but it's an easy illustration to start with.

const Width = ({ children }) => children(500)

The component calls children as a function, with some number of arguments. Here, it's the number 500.

To use this component, we give it a function as children.

<Width>
  {width => <div>window is {width}</div>}
</Width>

We get this output.

<div>window is 500</div>

With this setup, we can use this width to make rendering decisions.

<Width>
  {width =>
    width > 600
      ? <div>min-width requirement met!</div>
      : null
  }
</Width>

If we plan to use this condition a lot, we can define another components to encapsulate the reused logic.

const MinWidth = ({ width: minWidth, children }) =>
  <Width>
    {width =>
      width > minWidth
        ? children
        : null
    }
  </Width>

Obviously a static Width component isn't useful but one that watches the browser window is. Here's a sample implementation.

class WindowWidth extends React.Component {
  constructor() {
    super()
    this.state = { width: 0 }
  }

  componentDidMount() {
    this.setState(
      {width: window.innerWidth},
      window.addEventListener(
        "resize",
        ({ target }) =>
          this.setState({width: target.innerWidth})
      )
    )
  }

  render() {
    return this.props.children(this.state.width)
  }
}

Many developers favor Higher Order Components for this type of functionality. It's a matter of preference.

Children pass-through

There are times you'll need to wrap a stateless function with lifecycle events. While we want to wrap component functionality around other components, we don't want to introduce extraneous DOM nodes. In some apps, this might brake styling.

We use the function React.Children.only. only allows us to return this.props.children if there is only one child. Otherwise, it throws an error.

class SomeLifeCycleWrapper extends React.Component {
  componentDidMount() {
    console.log("I mounted but have no DOM.")
  }

  render() {
    return React.Children.only(this.props.children)
  }
}

In cases where you're working with state or context, prefer higher-order components or render callbacks.

Proxy component

(I'm not sure if this name makes sense)

Buttons are everywhere in web apps. And every one of them must have the type attribute set to "button".

<button type="button">

Writing this attribute hundreds of times is error prone. We can write a higher level component to proxy props to a lower-level button component.

const Button = props =>
  <button type="button" {...props}>

We can use Button in place of button and ensure that the type attribute is consistently applied everywhere.

<Button />
// <button type="button"><button>

<Button className="CTA">Send Money</Button>
// <button type="button" class="CTA">Send Money</button>

Style component

This is a Proxy component applied to the practices of style.

Say we have a button. It uses classes to be styled as a "primary" button.

<button type="button" className="btn btn-primary">

We can generate this output using a couple single-purpose components.

import classnames from 'classnames'

const PrimaryBtn = props =>
  <Btn {...props} primary />

const Btn = ({ className, primary, ...props }) =>
  <button
    type="button"
    className={classnames(
      "btn",
      primary && "btn-primary",
      className
    )}
    {...props}
  />

It can help to visualize this.

PrimaryBtn()
  ↳ Btn({primary: true})
    ↳ Button({className: "btn btn-primary"}, type: "button"})
      ↳ '<button type="button" class="btn btn-primary"></button>'

Using these components, all of these result in the same output.

<PrimaryBtn />
<Btn primary />
<button type="button" className="btn btn-primary" />

This can be a huge boon to style maintenance. It isolates all concerns of style to a single component.

Event switch

When writing event handlers it's common to adopt the handle{eventName} naming convention.

handleClick(e) { /* do something */ }

For components that handle several event types, these function names can be repetitive. The names themselves might not provide much value, as they simply proxy to other actions/functions.

handleClick() { require("./actions/doStuff")(/* action stuff */) }
handleMouseEnter() { this.setState({ hovered: true }) }
handleMouseLeave() { this.setState({ hovered: false }) }

Consider writing a single event handler for your component and switching on event.type.

handleEvent({type}) {
  switch(type) {
    case "click":
      return require("./actions/doStuff")(/* action dates */)
    case "mouseenter":
      return this.setState({ hovered: true })
    case "mouseleave":
      return this.setState({ hovered: false })
    default:
      return console.warn(`No case for event type "${type}"`)
  }
}

Alternatively, for simple components, you can call imported actions/functions directly from components, using arrow functions.

<div onClick={() => someImportedAction({ action: "DO_STUFF" })}

Don't fret about performance optimizations until you have problems. Seriously don't.

Layout component

Layout components result in some form of static DOM element. It might not need to update frequently, if ever.

Consider a component that renders two children side-by-side.

<HorizontalSplit
  leftSide={<SomeSmartComponent />}
  rightSide={<AnotherSmartComponent />}
/>

We can aggressively optimize this component.

While HorizontalSplit will be parent to both components, it will never be their owner. We can tell it to update never, without interrupting the lifecycle of the components inside.

class HorizontalSplit extends React.Component {
  shouldComponentUpdate() {
    return false
  }

  render() {
    <FlexContainer>
      <div>{this.props.leftSide}</div>
      <div>{this.props.rightSide}</div>
    </FlexContainer>
  }
}

Container component

"A container does data fetching and then renders its corresponding sub-component. That’s it."—Jason Bonta

Given this reusable CommentList component.

const CommentList = ({ comments }) =>
  <ul>
    {comments.map(comment =>
      <li>{comment.body}-{comment.author}</li>
    )}
  </ul>

We can create a new component responsible for fetching data and rendering the stateless CommentList component.

class CommentListContainer extends React.Component {
  constructor() {
    super()
    this.state = { comments: [] }
  }

  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: comments =>
        this.setState({comments: comments});
    })
  }

  render() {
    return <CommentList comments={this.state.comments} />
  }
}

We can write different containers for different application contexts.

Higher-order component

A higher-order function is a function that takes and/or returns a function. It's not more complicated than that. So, what's a higher-order component?

If you're already using container components, these are just generic containers, wrapped up in a function.

Let's start with our stateless Greeting component.

const Greeting = ({ name }) => {
  if (!name) { return <div>Connecting...</div> }

  return <div>Hi {name}!</div>
}

If it gets props.name, it's gonna render that data. Otherwise it'll say that it's "Connecting...". Now for the the higher-order bit.

const Connect = ComposedComponent =>
  class extends React.Component {
    constructor() {
      super()
      this.state = { name: "" }
    }

    componentDidMount() {
      // this would fetch or connect to a store
      this.setState({ name: "Michael" })
    }

    render() {
      return (
        <ComposedComponent
          {...this.props}
          name={this.state.name}
        />
      )
    }
  }

This is just a function that returns component that renders the component we passed as an argument.

Last step, we need to wrap our our Greeting component in Connect.

const ConnectedMyComponent = Connect(Greeting)

This is a powerful pattern for providing fetching and providing data to any number of stateless function components.

State hoisting

Stateless functions don't hold state (as the name implies).

Events are changes in state. Their data needs to be passed to stateful container components parents.

This is called "state hoisting". It's accomplished by passing a callback from a container component to a child component.

class NameContainer extends React.Component {
  render() {
    return <Name onChange={newName => alert(newName)} />
  }
}

const Name = ({ onChange }) =>
  <input onChange={e => onChange(e.target.value)} />

Name receives an onChange callback from NameContainer and calls on events.

The alert above makes for a terse demo but it's not changing state. Let's change the internal state of NameContainer.

class NameContainer extends React.Component {
  constructor() {
    super()
    this.state = {name: ""}
  }

  render() {
    return <Name onChange={newName => this.setState({name: newName})} />
  }
}

The state is hoisted to the container, by the provided callback, where it's used to update local state. This sets a nice clear boundary and maximizes the re-usability of stateless function.

This pattern isn't limited to stateless functions. Because stateless function don't have lifecycle events, you'll use this pattern with component classes as well.

Controlled input is an important pattern to know for use with state hoisting

(It's best to process the event object on the stateful component)

Controlled input

It's hard to talk about controlled inputs in the abstract. Let's start with an uncontrolled (normal) input and go from there.

<input type="text" />

When you fiddle with this input in the browser, you see your changes. This is normal.

A controlled input disallows the DOM mutations that make this possible. You set the value of the input in component-land and it doesn't change in DOM-land.

<input type="text" value="This won't change. Try it." />

Obviously static inputs aren't very useful to your users. So, we derive a value from state.

class ControlledNameInput extends React.Component {
  constructor() {
    super()
    this.state = {name: ""}
  }

  render() {
    return <input type="text" value={this.state.name} />
  }
}

Then, changing the input is a matter of changing component state.

    return (
      <input
        value={this.state.name}
        onChange={e => this.setState({ name: e.target.value })}
      />
    )

This is a controlled input. It only updates the DOM when state has changed in our component. This is invaluable when creating consistent UIs.

If you're using stateless functions for form elements, read about using state hoisting to move new state up the component tree.

More Repositories

1

react-patterns

moved to to https://github.com/chantastic/sites/tree/master/reactpatterns.com
1,698
star
2

sites

JavaScript
1,032
star
3

react-cheat-sheet

A filterable React.js reference site
JavaScript
181
star
4

minions.css

evil micro-classes
CSS
104
star
5

react-suspense-course

JavaScript
73
star
6

react-svg-spinner

An SVG spinner component
JavaScript
52
star
7

react-media-object

A media-object implementation in React.js
JavaScript
36
star
8

react-testing-patterns

Mostly reasonable patterns for testing React on Rails
34
star
9

practical-bem

The TL;DR on BEM IRL
34
star
10

8-point

A set of 8 point grid classes
CSS
28
star
11

commit-guidelines

How we commit code
23
star
12

react-rails-alt

A sample Rails application with React.js and Alt(Flux)
Ruby
22
star
13

react.holiday

A React advent for nerds and n00bs.
Astro
21
star
14

tip.css

A CSS tooltip.
CSS
16
star
15

css4-to-css3

An in-browser transformer for cssnext, in React. Just for funsies.
JavaScript
14
star
16

one-million-ui-states

A demonstration of how I test one million UI states every merge
JavaScript
13
star
17

use-lodash

A really terrible React Hook that is illustrative in other ways
JavaScript
11
star
18

reactcontext.com

a gentle intro to the what, where, when, why, and how of React Context
JavaScript
9
star
19

dt-service-calculator

A sample component with step-able history for training with Digital Telepathy — June 17, 2015
JavaScript
9
star
20

compare-words

A wordle guess-validation functions
JavaScript
8
star
21

btn.css

A scalable, style-able button.
HTML
7
star
22

prompt-guidelines

How to write user-friendly prompts
6
star
23

react-europa-inline-styles

My Keynote slide deck from React Europe 2015
6
star
24

react-flag-object

WIP React Flag Object implementation with inline-styles
JavaScript
5
star
25

react-disposition

Reusable, transportable, display and position React components.
JavaScript
5
star
26

updown

WORK IN PROGRESS!!!!!: A recursive README.md to index.html converter
JavaScript
5
star
27

react-card-object

A React card implementation with inline styles
JavaScript
5
star
28

rocss

Resource Oriented CSS
4
star
29

vite-react-jest-testing-library-axe

TypeScript
4
star
30

css-patterns

A mostly reasonable approach to naming things in CSS
4
star
31

style-components.com

HTML
4
star
32

myopic

A function for crafting thoughts that don't age well 🤓
JavaScript
4
star
33

ease.css

transition-timing functions as css classes
CSS
4
star
34

pseudo-class

JavaScript
4
star
35

select.css

HTML
3
star
36

legacy-dotfiles

Local extensions to Thoughtbot/dotfiles
Emacs Lisp
3
star
37

next-pokedex

A sample Next 13 app for react.holiday 2022
CSS
3
star
38

learnreactin30days

3
star
39

react-18-migration

WIP: Migrate a Client-Side React Apps to 18 Beta
JavaScript
3
star
40

worst

WIP
CSS
3
star
41

minions_rails

minions.css for Rails
CSS
3
star
42

react-minions

WIP. Don't use.
JavaScript
3
star
43

2016-reactjs-conf-faq

ARCHIVED: Things to know about getting a 2016 React.js Conf ticket
3
star
44

component-driven-avatar

JavaScript
2
star
45

circum

A positive array index for any number.
JavaScript
2
star
46

swifty.js

Better DevTools console
JavaScript
2
star
47

concurrent-mode-in-30

JavaScript
2
star
48

map-pseudo-classes

Using JS map() to implement CSS psuedo-classes
JavaScript
2
star
49

dotfiles

Shell
2
star
50

popover.css

popover styles
CSS
2
star
51

font-scale.css

CSS
2
star
52

frontend-guide

A Guide to PCO Frontend Tools
2
star
53

instruction.css

Exposable bits of UI clarification
JavaScript
2
star
54

rem-point

Soft-grid classes using rem
CSS
2
star
55

fullstack-react-todo

JavaScript
2
star
56

reactpatterns-book

2
star
57

dropdown.css

css dropdown WIP
JavaScript
2
star
58

storybook-nextjs

A setup run-thru of Storybook in a stock NextJS app
TypeScript
2
star
59

table.css

Responsive Table CSS
HTML
2
star
60

react-blox

Reusable, transportable components with box-model style props.
JavaScript
2
star
61

react_component_rails

A React component generator with love for tests and the Asset Pipeline
Ruby
2
star
62

react-aria-dropdown

WIP
2
star
63

react-basics

React Basics Book
2
star
64

aria-live-storybook-addon

Observe and log aria-live region changes in the addon panel
JavaScript
2
star
65

storybook-automated-tests-with-coverage

JavaScript
2
star
66

til

stuff i just learned
2
star
67

reactholiday22

A 25 day celebration of React at react.holiday
JavaScript
2
star
68

compare-words-worker

A Cloudflare Worker that compares words a la Wordle
JavaScript
2
star
69

react-starter

I've done this like 500 times...
JavaScript
2
star
70

birdcallreview.com

HTML
2
star
71

react-prompt

WIP
JavaScript
2
star
72

chantastic.org

JavaScript
2
star
73

chansolo.com

i picture of me superimposed on han solo
HTML
2
star
74

system-ui.css

An opinionated system-ui fallback, exposed as a class
CSS
2
star
75

planningcenter-minions.css

A minions.css extensions for Planning Center app colors.
HTML
2
star
76

point.css

WIP: relative point layout system in CSS
CSS
1
star
77

minionize_ruby

media-query helper for minions.css
Ruby
1
star
78

immutable-react

Immutable React Playground
JavaScript
1
star
79

reactbasics.com

A gentle introduction to React
1
star
80

use-aria-live

JavaScript
1
star
81

open-color.css

CSS
1
star
82

esmodule-reference-course

A short but thorough introduction to ES Modules
JavaScript
1
star
83

supabase-nextjs

JavaScript
1
star
84

storybook-react-portals-demo

Stories showcasing React Portals and how to interact with them declaratively
TypeScript
1
star
85

es-react-snippets.el

WIP
Emacs Lisp
1
star
86

batman-touch

jQuery Mobile touch-events extension for Batman.js
CoffeeScript
1
star
87

restated

JavaScript
1
star
88

elements

Created with CodeSandbox
JavaScript
1
star
89

instruct

JavaScript
1
star
90

wordle-words

The dictionary of words used in Wordle the word game
JavaScript
1
star
91

chantastic

My contact, on the NPM registry
JavaScript
1
star
92

givingashit.com

Personal Droning
CSS
1
star
93

button.css

A mostly reasonable button
CSS
1
star
94

chantastic.io

CSS
1
star
95

emacs

Learning emacs and shit
CSS
1
star
96

doxy

Cross-platform Church Center Components
JavaScript
1
star
97

pages-playground

playing with gh-pages features
HTML
1
star
98

stack.css

A WIP mobile-first grid concept
CSS
1
star
99

dialog.css

CSS Dialog
CSS
1
star
100

snippets-sublime-toolkitv5

Emporium Plus TOOL KIT v5 Snippet Library for Sublime Text
1
star