{"id":13394675,"url":"https://github.com/chantastic/react-patterns","last_synced_at":"2025-03-31T04:03:09.639Z","repository":{"id":20395431,"uuid":"23671297","full_name":"chantastic/react-patterns","owner":"chantastic","description":"moved to to https://github.com/chantastic/sites/tree/master/reactpatterns.com","archived":false,"fork":false,"pushed_at":"2019-07-10T00:05:33.000Z","size":64,"stargazers_count":1684,"open_issues_count":3,"forks_count":111,"subscribers_count":72,"default_branch":"master","last_synced_at":"2025-03-24T03:47:27.585Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://reactpatterns.com","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chantastic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-04T17:00:29.000Z","updated_at":"2025-02-21T23:28:54.000Z","dependencies_parsed_at":"2022-07-31T18:38:11.221Z","dependency_job_id":null,"html_url":"https://github.com/chantastic/react-patterns","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Freact-patterns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Freact-patterns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Freact-patterns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Freact-patterns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chantastic","download_url":"https://codeload.github.com/chantastic/react-patterns/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246413231,"owners_count":20773053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:01:27.848Z","updated_at":"2025-03-31T04:03:09.620Z","avatar_url":"https://github.com/chantastic.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"NO LONGER MAINTAINED  \n===\nMoved and updated at [reactpatterns.com](https://reactpatterns.com).\nThanks for your help in developing this into the site.\n\n---\n\nReact\n=====\n\n*Mostly reasonable patterns for writing React on Rails*\n\n## Table of Contents\n\n1. [Scope](#scope)\n1. Organization\n  1. [Component Organization](#component-organization)\n  1. [Formatting Props](#formatting-props)\n1. Patterns\n  1. [Computed Props](#computed-props)\n  1. [Compound State](#compound-state)\n  1. [prefer-ternary-to-sub-render](#prefer-ternary-to-sub-render)\n  1. [View Components](#view-components)\n  1. [Container Components](#container-components)\n1. Anti-patterns\n  1. [Compound Conditions](#compound-conditions)\n  1. [Cached State in render](#cached-state-in-render)\n  1. [Existence Checking](#existence-checking)\n  1. [Setting State from Props](#setting-state-from-props)\n1. Practices\n  1. [Naming Handle Methods](#naming-handler-methods)\n  1. [Naming Events](#naming-events)\n  1. [Using PropTypes](#using-proptypes)\n  1. [Using Entities](#using-entities)\n1. Gotchas\n  1. [Tables](#tables)\n1. Libraries\n  1. [classnames](#classnames)\n1. Other\n  1. [JSX](#jsx)\n  1. [ES2015](#es2015)\n  1. [react-rails](#react-rails)\n  1. [rails-assets](#rails-assets)\n  1. [flux](#flux)\n\n---\n\n## Scope\n\nThis is how we write [React.js](https://facebook.github.io/react/) on Rails.\nWe've struggled to find the happy path. Recommendations here represent a good\nnumber of failed attempts. If something seems out of place, it probably is;\nlet us know what you've found.\n\nAll examples written in ES2015 syntax now that the\n[official react-rails gem](https://github.com/reactjs/react-rails) ships with\n[babel](http://babeljs.io/).\n\n**[⬆ back to top](#table-of-contents)**\n\n---\n\n## Component Organization\n\n* class definition\n  * constructor\n    * event handlers\n  * 'component' lifecycle events\n  * getters\n  * render\n* defaultProps\n* proptypes\n\n```javascript\nclass Person extends React.Component {\n  constructor (props) {\n    super(props);\n\n    this.state = { smiling: false };\n\n    this.handleClick = () =\u003e {\n      this.setState({smiling: !this.state.smiling});\n    };\n  }\n\n  componentWillMount () {\n    // add event listeners (Flux Store, WebSocket, document, etc.)\n  }\n\n  componentDidMount () {\n    // React.getDOMNode()\n  }\n\n  componentWillUnmount () {\n    // remove event listeners (Flux Store, WebSocket, document, etc.)\n  }\n\n  get smilingMessage () {\n    return (this.state.smiling) ? \"is smiling\" : \"\";\n  }\n\n  render () {\n    return (\n      \u003cdiv onClick={this.handleClick}\u003e\n        {this.props.name} {this.smilingMessage}\n      \u003c/div\u003e\n    );\n  }\n}\n\nPerson.defaultProps = {\n  name: 'Guest'\n};\n\nPerson.propTypes = {\n  name: React.PropTypes.string\n};\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Formatting Props\n\nWrap props on newlines for exactly 2 or more.\n\n```html\n// bad\n\u003cPerson\n firstName=\"Michael\" /\u003e\n\n// good\n\u003cPerson firstName=\"Michael\" /\u003e\n```\n\n```html\n// bad\n\u003cPerson firstName=\"Michael\" lastName=\"Chan\" occupation=\"Designer\" favoriteFood=\"Drunken Noodles\" /\u003e\n\n// good\n\u003cPerson\n firstName=\"Michael\"\n lastName=\"Chan\"\n occupation=\"Designer\"\n favoriteFood=\"Drunken Noodles\" /\u003e\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n---\n\n## Computed Props\n\nUse getters to name computed properties.\n\n```javascript\n  // bad\n  firstAndLastName () {\n    return `${this.props.firstName} ${this.props.lastName}`;\n  }\n\n  // good\n  get fullName () {\n    return `${this.props.firstName} ${this.props.lastName}`;\n  }\n```\n\nSee: [Cached State in render](#cached-state-in-render) anti-pattern\n\n**[⬆ back to top](#table-of-contents)**\n\n---\n\n## Compound State\n\nPrefix compound state getters with a verb for readability.\n\n```javascript\n// bad\nhappyAndKnowsIt () {\n  return this.state.happy \u0026\u0026 this.state.knowsIt;\n}\n```\n\n```javascript\n// good\nget isHappyAndKnowsIt () {\n  return this.state.happy \u0026\u0026 this.state.knowsIt;\n}\n```\n\nThese methods *MUST* return a `boolean` value.\n\nSee: [Compound Conditions](#compound-conditions) anti-pattern\n\n**[⬆ back to top](#table-of-contents)**\n\n## Prefer Ternary to Sub-render\n\nKeep logic inside the `render` function.\n\n```javascript\n// bad\nrenderSmilingStatement () {\n  return \u003cstrong\u003e{(this.state.isSmiling) ? \" is smiling.\" : \"\"}\u003c/strong\u003e;\n},\n\nrender () {\n  return \u003cdiv\u003e{this.props.name}{this.renderSmilingStatement()}\u003c/div\u003e;\n}\n```\n\n```javascript\n// good\nrender () {\n  return (\n    \u003cdiv\u003e\n      {this.props.name}\n      {(this.state.smiling)\n        ? \u003cspan\u003eis smiling\u003c/span\u003e\n        : null\n      }\n    \u003c/div\u003e\n  );\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## View Components\n\nCompose components into views. Don't create one-off components that merge layout\nand domain components.\n\n```javascript\n// bad\nclass PeopleWrappedInBSRow extends React.Component {\n  render () {\n    return (\n      \u003cdiv className=\"row\"\u003e\n        \u003cPeople people={this.state.people} /\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n```javascript\n// good\nclass BSRow extends React.Component {\n  render () {\n    return \u003cdiv className=\"row\"\u003e{this.props.children}\u003c/div\u003e;\n  }\n}\n\nclass SomeView extends React.Component {\n  render () {\n    return (\n      \u003cBSRow\u003e\n        \u003cPeople people={this.state.people} /\u003e\n      \u003c/BSRow\u003e\n    );\n  }\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Container Components\n\n\u003e A container does data fetching and then renders its corresponding\n\u003e sub-component. That's it. \u0026mdash; Jason Bonta\n\n#### Bad\n\n```javascript\n// CommentList.js\n\nclass CommentList extends React.Component {\n  getInitialState () {\n    return { comments: [] };\n  }\n\n  componentDidMount () {\n    $.ajax({\n      url: \"/my-comments.json\",\n      dataType: 'json',\n      success: function(comments) {\n        this.setState({comments: comments});\n      }.bind(this)\n    });\n  }\n\n  render () {\n    return (\n      \u003cul\u003e\n        {this.state.comments.map(({body, author}) =\u003e {\n          return \u003cli\u003e{body}—{author}\u003c/li\u003e;\n        })}\n      \u003c/ul\u003e\n    );\n  }\n}\n```\n\n#### Good\n\n```javascript\n// CommentList.js\n\nclass CommentList extends React.Component {\n  render() {\n    return (\n      \u003cul\u003e\n        {this.props.comments.map(({body, author}) =\u003e {\n          return \u003cli\u003e{body}—{author}\u003c/li\u003e;\n        })}\n      \u003c/ul\u003e\n    );\n  }\n}\n```\n\n```javascript\n// CommentListContainer.js\n\nclass CommentListContainer extends React.Component {\n  getInitialState () {\n    return { comments: [] }\n  }\n\n  componentDidMount () {\n    $.ajax({\n      url: \"/my-comments.json\",\n      dataType: 'json',\n      success: function(comments) {\n        this.setState({comments: comments});\n      }.bind(this)\n    });\n  }\n\n  render () {\n    return \u003cCommentList comments={this.state.comments} /\u003e;\n  }\n}\n```\n\n[Read more](https://medium.com/@learnreact/container-components-c0e67432e005)  \n[Watch more](https://www.youtube.com/watch?v=KYzlpRvWZ6c\u0026t=1351)\n\n**[⬆ back to top](#table-of-contents)**\n\n---\n\n## Cached State in `render`\n\nDo not keep state in `render`\n\n```javascript\n// bad\nrender () {\n  let name = `Mrs. ${this.props.name}`;\n\n  return \u003cdiv\u003e{name}\u003c/div\u003e;\n}\n\n// good\nrender () {\n  return \u003cdiv\u003e{`Mrs. ${this.props.name}`}\u003c/div\u003e;\n}\n```\n\n```javascript\n// best\nget fancyName () {\n  return `Mrs. ${this.props.name}`;\n}\n\nrender () {\n  return \u003cdiv\u003e{this.fancyName}\u003c/div\u003e;\n}\n```\n\n*This is mostly stylistic and keeps diffs nice. I doubt that there's a significant perf reason to do this.*\n\nSee: [Computed Props](#computed-props) pattern\n\n**[⬆ back to top](#table-of-contents)**\n\n## Compound Conditions\n\nDon't put compound conditions in `render`.\n\n```javascript\n// bad\nrender () {\n  return \u003cdiv\u003e{if (this.state.happy \u0026\u0026 this.state.knowsIt) { return \"Clapping hands\" }\u003c/div\u003e;\n}\n```\n\n```javascript\n// better\nget isTotesHappy() {\n  return this.state.happy \u0026\u0026 this.state.knowsIt;\n},\n\nrender() {\n  return \u003cdiv\u003e{(this.isTotesHappy) \u0026\u0026 \"Clapping hands\"}\u003c/div\u003e;\n}\n```\n\nThe best solution for this would use a [container\ncomponent](#container-components) to manage state and\npass new state down as props.\n\nSee: [Compound State](#compound-state) pattern\n\n**[⬆ back to top](#table-of-contents)**\n\n## Existence Checking\n\nDo not check existence of props at the root of a component.\nComponents should not have two possible return types.\n\n```javascript\n// bad\nconst Person = props =\u003e {\n  if (this.props.firstName)\n    return \u003cdiv\u003e{this.props.firstName}\u003c/div\u003e\n  else\n    return null\n}\n```\n\nComponents should *always* render. Consider adding `defaultProps`, where a sensible default is appropriate.\n\n```javascript\n// better\nconst Person = props =\u003e\n  \u003cdiv\u003e{this.props.firstName}\u003c/div\u003e\n\nPerson.defaultProps = {\n  firstName: \"Guest\"\n}\n```\n\nIf a component should be conditionally rendered, handle that in the owner component.\n\n```javascript\n// best\nconst TheOwnerComponent = props =\u003e\n  \u003cdiv\u003e\n    {props.person \u0026\u0026 \u003cPerson {...props.person} /\u003e}\n  \u003c/div\u003e\n```\n\nThis is only where objects or arrays are used. Use PropTypes.shape to clarify\nthe types of nested data expected by the component.\n\n**[⬆ back to top](#table-of-contents)**\n\n## Setting State from Props\n\nDo not set state from props without obvious intent.\n\n```javascript\n// bad\ngetInitialState () {\n  return {\n    items: this.props.items\n  };\n}\n```\n\n```javascript\n// good\ngetInitialState () {\n  return {\n    items: this.props.initialItems\n  };\n}\n```\n\nRead: [\"Props in getInitialState Is an Anti-Pattern\"](http://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html)\n\n**[⬆ back to top](#table-of-contents)**\n\n---\n\n## Naming Handler Methods\n\nName the handler methods after their triggering event.\n\n```javascript\n// bad\npunchABadger () { /*...*/ },\n\nrender () {\n  return \u003cdiv onClick={this.punchABadger} /\u003e;\n}\n```\n\n```javascript\n// good\nhandleClick () { /*...*/ },\n\nrender () {\n  return \u003cdiv onClick={this.handleClick} /\u003e;\n}\n```\n\nHandler names should:\n\n- begin with `handle`\n- end with the name of the event they handle (eg, `Click`, `Change`)\n- be present-tense\n\nIf you need to disambiguate handlers, add additional information between\n`handle` and the event name. For example, you can distinguish between `onChange`\nhandlers: `handleNameChange` and `handleAgeChange`. When you do this, ask\nyourself if you should be creating a new component.\n\n**[⬆ back to top](#table-of-contents)**\n\n## Naming Events\n\nUse custom event names for ownee events.\n\n```javascript\nclass Owner extends React.Component {\n  handleDelete () {\n    // handle Ownee's onDelete event\n  }\n\n  render () {\n    return \u003cOwnee onDelete={this.handleDelete} /\u003e;\n  }\n}\n\nclass Ownee extends React.Component {\n  render () {\n    return \u003cdiv onChange={this.props.onDelete} /\u003e;\n  }\n}\n\nOwnee.propTypes = {\n  onDelete: React.PropTypes.func.isRequired\n};\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Using PropTypes\n\nUse PropTypes to communicate expectations and log meaningful warnings.\n\n```javascript\nMyValidatedComponent.propTypes = {\n  name: React.PropTypes.string\n};\n```\n`MyValidatedComponent` will log a warning if it receives `name` of a type other than `string`.\n\n\n```html\n\u003cPerson name=1337 /\u003e\n// Warning: Invalid prop `name` of type `number` supplied to `MyValidatedComponent`, expected `string`.\n```\n\nComponents may also require `props`.\n\n```javascript\nMyValidatedComponent.propTypes = {\n  name: React.PropTypes.string.isRequired\n}\n```\n\nThis component will now validate the presence of name.\n\n```html\n\u003cPerson /\u003e\n// Warning: Required prop `name` was not specified in `Person`\n```\n\nRead: [Prop Validation](http://facebook.github.io/react/docs/reusable-components.html#prop-validation)\n\n**[⬆ back to top](#table-of-contents)**\n\n## Using Entities\n\nUse React's `String.fromCharCode()` for special characters.\n\n```javascript\n// bad\n\u003cdiv\u003ePiCO · Mascot\u003c/div\u003e\n\n// nope\n\u003cdiv\u003ePiCO \u0026middot; Mascot\u003c/div\u003e\n\n// good\n\u003cdiv\u003e{'PiCO ' + String.fromCharCode(183) + ' Mascot'}\u003c/div\u003e\n\n// better\n\u003cdiv\u003e{`PiCO ${String.fromCharCode(183)} Mascot`}\u003c/div\u003e\n```\n\nRead: [JSX Gotchas](http://facebook.github.io/react/docs/jsx-gotchas.html#html-entities)\n\n**[⬆ back to top](#table-of-contents)**\n\n## Tables\n\nThe browser thinks you're dumb. But React doesn't. Always use `tbody` in\n`table` components.\n\n```javascript\n// bad\nrender () {\n  return (\n    \u003ctable\u003e\n      \u003ctr\u003e...\u003c/tr\u003e\n    \u003c/table\u003e\n  );\n}\n\n// good\nrender () {\n  return (\n    \u003ctable\u003e\n      \u003ctbody\u003e\n        \u003ctr\u003e...\u003c/tr\u003e\n      \u003c/tbody\u003e\n    \u003c/table\u003e\n  );\n}\n```\n\nThe browser is going to insert `tbody` if you forget. React will continue to\ninsert new `tr`s into the `table` and confuse the heck out of you. Always use\n`tbody`.\n\n**[⬆ back to top](#table-of-contents)**\n\n## classnames\n\nUse [classNames](https://www.npmjs.com/package/classnames) to manage conditional classes.\n\n```javascript\n// bad\nget classes () {\n  let classes = ['MyComponent'];\n\n  if (this.state.active) {\n    classes.push('MyComponent--active');\n  }\n\n  return classes.join(' ');\n}\n\nrender () {\n  return \u003cdiv className={this.classes} /\u003e;\n}\n```\n\n```javascript\n// good\nrender () {\n  let classes = {\n    'MyComponent': true,\n    'MyComponent--active': this.state.active\n  };\n\n  return \u003cdiv className={classnames(classes)} /\u003e;\n}\n```\n\nRead: [Class Name Manipulation](https://github.com/JedWatson/classnames/blob/master/README.md)\n\n**[⬆ back to top](#table-of-contents)**\n\n## JSX\n\nWe used to have some hardcore CoffeeScript lovers is the group. The unfortunate\nthing about writing templates in CoffeeScript is that it leaves you on the hook\nwhen certain implementations changes that JSX would normally abstract.\n\nWe no longer recommend using CoffeeScript to write `render`.\n\nFor posterity, you can read about how we used CoffeeScript, when using CoffeeScript was\nnon-negotiable: [CoffeeScript and JSX](https://slack-files.com/T024L9M0Y-F02HP4JM3-80d714).\n\n**[⬆ back to top](#table-of-contents)**\n\n## ES2015\n\n[react-rails](https://github.com/reactjs/react-rails) now ships with [babel](babeljs.io). Anything\nyou can do in Babel, you can do in Rails. See the documentation for additional config.\n\n**[⬆ back to top](#table-of-contents)**\n\n## react-rails\n\n[react-rails](https://github.com/reactjs/react-rails) should be used in all\nRails apps that use React. It provides the perfect amount of glue between Rails\nconventions and React.\n\n**[⬆ back to top](#table-of-contents)**\n\n## rails-assets\n[rails-assets](https://rails-assets.org) should be considered for bundling\njs/css assets into your applications. The most popular React-libraries we use\nare registered on [Bower](http://bower.io) and can be easily added through\nBundler and react-assets.\n\n**caveats: rails-assets gives you access to bower projects via Sprockets\nrequires. This is a win for the traditionally hand-wavy approach that Rails\ntakes with JavaScript. This approach doesn't buy you modularity or the ability to\ninterop with JS tooling that requires modules.**\n\n**[⬆ back to top](#table-of-contents)**\n\n## flux\n\nUse [Alt](http://alt.js.org) for flux implementation. Alt is true to the flux\npattern with the best documentation available.\n\n**[⬆ back to top](#table-of-contents)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchantastic%2Freact-patterns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchantastic%2Freact-patterns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchantastic%2Freact-patterns/lists"}