• Stars
    star
    1,698
  • Rank 27,486 (Top 0.6 %)
  • Language
  • License
    MIT License
  • Created about 10 years ago
  • Updated over 5 years ago

Reviews

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

Repository Details

moved to to https://github.com/chantastic/sites/tree/master/reactpatterns.com

NO LONGER MAINTAINED

Moved and updated at reactpatterns.com. Thanks for your help in developing this into the site.


React

Mostly reasonable patterns for writing React on Rails

Table of Contents

  1. Scope
  2. Organization
  3. Component Organization
  4. Formatting Props
  5. Patterns
  6. Computed Props
  7. Compound State
  8. prefer-ternary-to-sub-render
  9. View Components
  10. Container Components
  11. Anti-patterns
  12. Compound Conditions
  13. Cached State in render
  14. Existence Checking
  15. Setting State from Props
  16. Practices
  17. Naming Handle Methods
  18. Naming Events
  19. Using PropTypes
  20. Using Entities
  21. Gotchas
  22. Tables
  23. Libraries
  24. classnames
  25. Other
  26. JSX
  27. ES2015
  28. react-rails
  29. rails-assets
  30. flux

Scope

This is how we write React.js on Rails. We've struggled to find the happy path. Recommendations here represent a good number of failed attempts. If something seems out of place, it probably is; let us know what you've found.

All examples written in ES2015 syntax now that the official react-rails gem ships with babel.

⬆ back to top


Component Organization

  • class definition
    • constructor
      • event handlers
    • 'component' lifecycle events
    • getters
    • render
  • defaultProps
  • proptypes
class Person extends React.Component {
  constructor (props) {
    super(props);

    this.state = { smiling: false };

    this.handleClick = () => {
      this.setState({smiling: !this.state.smiling});
    };
  }

  componentWillMount () {
    // add event listeners (Flux Store, WebSocket, document, etc.)
  }

  componentDidMount () {
    // React.getDOMNode()
  }

  componentWillUnmount () {
    // remove event listeners (Flux Store, WebSocket, document, etc.)
  }

  get smilingMessage () {
    return (this.state.smiling) ? "is smiling" : "";
  }

  render () {
    return (
      <div onClick={this.handleClick}>
        {this.props.name} {this.smilingMessage}
      </div>
    );
  }
}

Person.defaultProps = {
  name: 'Guest'
};

Person.propTypes = {
  name: React.PropTypes.string
};

⬆ back to top

Formatting Props

Wrap props on newlines for exactly 2 or more.

// bad
<Person
 firstName="Michael" />

// good
<Person firstName="Michael" />
// bad
<Person firstName="Michael" lastName="Chan" occupation="Designer" favoriteFood="Drunken Noodles" />

// good
<Person
 firstName="Michael"
 lastName="Chan"
 occupation="Designer"
 favoriteFood="Drunken Noodles" />

⬆ back to top


Computed Props

Use getters to name computed properties.

  // bad
  firstAndLastName () {
    return `${this.props.firstName} ${this.props.lastName}`;
  }

  // good
  get fullName () {
    return `${this.props.firstName} ${this.props.lastName}`;
  }

See: Cached State in render anti-pattern

⬆ back to top


Compound State

Prefix compound state getters with a verb for readability.

// bad
happyAndKnowsIt () {
  return this.state.happy && this.state.knowsIt;
}
// good
get isHappyAndKnowsIt () {
  return this.state.happy && this.state.knowsIt;
}

These methods MUST return a boolean value.

See: Compound Conditions anti-pattern

⬆ back to top

Prefer Ternary to Sub-render

Keep logic inside the render function.

// bad
renderSmilingStatement () {
  return <strong>{(this.state.isSmiling) ? " is smiling." : ""}</strong>;
},

render () {
  return <div>{this.props.name}{this.renderSmilingStatement()}</div>;
}
// good
render () {
  return (
    <div>
      {this.props.name}
      {(this.state.smiling)
        ? <span>is smiling</span>
        : null
      }
    </div>
  );
}

⬆ back to top

View Components

Compose components into views. Don't create one-off components that merge layout and domain components.

// bad
class PeopleWrappedInBSRow extends React.Component {
  render () {
    return (
      <div className="row">
        <People people={this.state.people} />
      </div>
    );
  }
}
// good
class BSRow extends React.Component {
  render () {
    return <div className="row">{this.props.children}</div>;
  }
}

class SomeView extends React.Component {
  render () {
    return (
      <BSRow>
        <People people={this.state.people} />
      </BSRow>
    );
  }
}

⬆ back to top

Container Components

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

Bad

// CommentList.js

class CommentList extends React.Component {
  getInitialState () {
    return { comments: [] };
  }

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

  render () {
    return (
      <ul>
        {this.state.comments.map(({body, author}) => {
          return <li>{body}—{author}</li>;
        })}
      </ul>
    );
  }
}

Good

// CommentList.js

class CommentList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.comments.map(({body, author}) => {
          return <li>{body}—{author}</li>;
        })}
      </ul>
    );
  }
}
// CommentListContainer.js

class CommentListContainer extends React.Component {
  getInitialState () {
    return { comments: [] }
  }

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

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

Read more
Watch more

⬆ back to top


Cached State in render

Do not keep state in render

// bad
render () {
  let name = `Mrs. ${this.props.name}`;

  return <div>{name}</div>;
}

// good
render () {
  return <div>{`Mrs. ${this.props.name}`}</div>;
}
// best
get fancyName () {
  return `Mrs. ${this.props.name}`;
}

render () {
  return <div>{this.fancyName}</div>;
}

This is mostly stylistic and keeps diffs nice. I doubt that there's a significant perf reason to do this.

See: Computed Props pattern

⬆ back to top

Compound Conditions

Don't put compound conditions in render.

// bad
render () {
  return <div>{if (this.state.happy && this.state.knowsIt) { return "Clapping hands" }</div>;
}
// better
get isTotesHappy() {
  return this.state.happy && this.state.knowsIt;
},

render() {
  return <div>{(this.isTotesHappy) && "Clapping hands"}</div>;
}

The best solution for this would use a container component to manage state and pass new state down as props.

See: Compound State pattern

⬆ back to top

Existence Checking

Do not check existence of props at the root of a component. Components should not have two possible return types.

// bad
const Person = props => {
  if (this.props.firstName)
    return <div>{this.props.firstName}</div>
  else
    return null
}

Components should always render. Consider adding defaultProps, where a sensible default is appropriate.

// better
const Person = props =>
  <div>{this.props.firstName}</div>

Person.defaultProps = {
  firstName: "Guest"
}

If a component should be conditionally rendered, handle that in the owner component.

// best
const TheOwnerComponent = props =>
  <div>
    {props.person && <Person {...props.person} />}
  </div>

This is only where objects or arrays are used. Use PropTypes.shape to clarify the types of nested data expected by the component.

⬆ back to top

Setting State from Props

Do not set state from props without obvious intent.

// bad
getInitialState () {
  return {
    items: this.props.items
  };
}
// good
getInitialState () {
  return {
    items: this.props.initialItems
  };
}

Read: "Props in getInitialState Is an Anti-Pattern"

⬆ back to top


Naming Handler Methods

Name the handler methods after their triggering event.

// bad
punchABadger () { /*...*/ },

render () {
  return <div onClick={this.punchABadger} />;
}
// good
handleClick () { /*...*/ },

render () {
  return <div onClick={this.handleClick} />;
}

Handler names should:

  • begin with handle
  • end with the name of the event they handle (eg, Click, Change)
  • be present-tense

If you need to disambiguate handlers, add additional information between handle and the event name. For example, you can distinguish between onChange handlers: handleNameChange and handleAgeChange. When you do this, ask yourself if you should be creating a new component.

⬆ back to top

Naming Events

Use custom event names for ownee events.

class Owner extends React.Component {
  handleDelete () {
    // handle Ownee's onDelete event
  }

  render () {
    return <Ownee onDelete={this.handleDelete} />;
  }
}

class Ownee extends React.Component {
  render () {
    return <div onChange={this.props.onDelete} />;
  }
}

Ownee.propTypes = {
  onDelete: React.PropTypes.func.isRequired
};

⬆ back to top

Using PropTypes

Use PropTypes to communicate expectations and log meaningful warnings.

MyValidatedComponent.propTypes = {
  name: React.PropTypes.string
};

MyValidatedComponent will log a warning if it receives name of a type other than string.

<Person name=1337 />
// Warning: Invalid prop `name` of type `number` supplied to `MyValidatedComponent`, expected `string`.

Components may also require props.

MyValidatedComponent.propTypes = {
  name: React.PropTypes.string.isRequired
}

This component will now validate the presence of name.

<Person />
// Warning: Required prop `name` was not specified in `Person`

Read: Prop Validation

⬆ back to top

Using Entities

Use React's String.fromCharCode() for special characters.

// bad
<div>PiCO · Mascot</div>

// nope
<div>PiCO &middot; Mascot</div>

// good
<div>{'PiCO ' + String.fromCharCode(183) + ' Mascot'}</div>

// better
<div>{`PiCO ${String.fromCharCode(183)} Mascot`}</div>

Read: JSX Gotchas

⬆ back to top

Tables

The browser thinks you're dumb. But React doesn't. Always use tbody in table components.

// bad
render () {
  return (
    <table>
      <tr>...</tr>
    </table>
  );
}

// good
render () {
  return (
    <table>
      <tbody>
        <tr>...</tr>
      </tbody>
    </table>
  );
}

The browser is going to insert tbody if you forget. React will continue to insert new trs into the table and confuse the heck out of you. Always use tbody.

⬆ back to top

classnames

Use classNames to manage conditional classes.

// bad
get classes () {
  let classes = ['MyComponent'];

  if (this.state.active) {
    classes.push('MyComponent--active');
  }

  return classes.join(' ');
}

render () {
  return <div className={this.classes} />;
}
// good
render () {
  let classes = {
    'MyComponent': true,
    'MyComponent--active': this.state.active
  };

  return <div className={classnames(classes)} />;
}

Read: Class Name Manipulation

⬆ back to top

JSX

We used to have some hardcore CoffeeScript lovers is the group. The unfortunate thing about writing templates in CoffeeScript is that it leaves you on the hook when certain implementations changes that JSX would normally abstract.

We no longer recommend using CoffeeScript to write render.

For posterity, you can read about how we used CoffeeScript, when using CoffeeScript was non-negotiable: CoffeeScript and JSX.

⬆ back to top

ES2015

react-rails now ships with babel. Anything you can do in Babel, you can do in Rails. See the documentation for additional config.

⬆ back to top

react-rails

react-rails should be used in all Rails apps that use React. It provides the perfect amount of glue between Rails conventions and React.

⬆ back to top

rails-assets

rails-assets should be considered for bundling js/css assets into your applications. The most popular React-libraries we use are registered on Bower and can be easily added through Bundler and react-assets.

caveats: rails-assets gives you access to bower projects via Sprockets requires. This is a win for the traditionally hand-wavy approach that Rails takes with JavaScript. This approach doesn't buy you modularity or the ability to interop with JS tooling that requires modules.

⬆ back to top

flux

Use Alt for flux implementation. Alt is true to the flux pattern with the best documentation available.

⬆ back to top

More Repositories

1

reactpatterns

Patterns for React Developers
HTML
1,724
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