react-style-guide.md (5704B)
1 ## React Style Guide 2 3 > This style guide comes as an addition to [Airbnb React/JSX Guide](https://github.com/airbnb/javascript/tree/master/react). 4 > Feel free to modify it to suit your project's needs. 5 6 ### Table of Contents 7 8 * [Separate folder per UI component](#separate-folder-per-ui-component) 9 * [Prefer using functional components](#prefer-using-functional-components) 10 * [Use CSS Modules](#use-css-modules) 11 * [Use higher-order components](#use-higher-order-components) 12 13 ### Separate folder per UI component 14 15 * Place each major UI component along with its resources in a separate folder<br> 16 This will make it easier to find related resources for any particular UI 17 element (CSS, images, unit tests, localization files etc.). Removing such 18 components during refactorings should also be easy. 19 * Avoid having CSS, images and other resource files shared between multiple components.<br> 20 This will make your code more maintainable, easy to refactor. 21 * Add `package.json` file into each component's folder.<br> 22 This will allow to easily reference such components from other places in 23 your code.<br> 24 Use `import Nav from '../Navigation'` instead of `import Nav from '../Navigation/Navigation.js'` 25 26 ``` 27 /components/Navigation/icon.svg 28 /components/Navigation/Navigation.css 29 /components/Navigation/Navigation.js 30 /components/Navigation/Navigation.test.js 31 /components/Navigation/Navigation.ru-RU.css 32 /components/Navigation/package.json 33 ``` 34 35 ``` 36 // components/Navigation/package.json 37 { 38 "name:": "Navigation", 39 "main": "./Navigation.js" 40 } 41 ``` 42 43 For more information google for [component-based UI development](https://google.com/search?q=component-based+ui+development). 44 45 ### Prefer using functional components 46 47 * Prefer using stateless functional components whenever possible.<br> 48 Components that don't use state are better to be written as simple pure functions. 49 50 ```jsx 51 // Bad 52 class Navigation extends Component { 53 static propTypes = { items: PropTypes.array.isRequired }; 54 render() { 55 return <nav><ul>{this.props.items.map(x => <li>{x.text}</li>)}</ul></nav>; 56 } 57 } 58 59 // Better 60 function Navigation({ items }) { 61 return ( 62 <nav><ul>{items.map(x => <li>{x.text}</li>)}</ul></nav>; 63 ); 64 } 65 Navigation.propTypes = { items: PropTypes.array.isRequired }; 66 ``` 67 68 ### Use CSS Modules 69 70 * Use CSS Modules<br> 71 This will allow using short CSS class names and at the same time avoid conflicts. 72 * Keep CSS simple and declarative. Avoid loops, mixins etc. 73 * Feel free to use variables in CSS via [precss](https://github.com/jonathantneal/precss) plugin for [PostCSS](https://github.com/postcss/postcss) 74 * Prefer CSS class selectors instead of element and `id` selectors (see [BEM](https://bem.info/)) 75 * Avoid nested CSS selectors (see [BEM](https://bem.info/)) 76 * When in doubt, use `.root { }` class name for the root elements of your components 77 78 ```scss 79 // Navigation.scss 80 @import '../variables.scss'; 81 82 .root { 83 width: 300px; 84 } 85 86 .items { 87 margin: 0; 88 padding: 0; 89 list-style-type: none; 90 text-align: center; 91 } 92 93 .item { 94 display: inline-block; 95 vertical-align: top; 96 } 97 98 .link { 99 display: block; 100 padding: 0 25px; 101 outline: 0; 102 border: 0; 103 color: $default-color; 104 text-decoration: none; 105 line-height: 25px; 106 transition: background-color .3s ease; 107 108 &, 109 .items:hover & { 110 background: $default-bg-color; 111 } 112 113 .selected, 114 .items:hover &:hover { 115 background: $active-bg-color; 116 } 117 } 118 ``` 119 120 ```jsx 121 // Navigation.js 122 import React, { PropTypes } from 'react'; 123 import withStyles from 'isomorphic-style-loader/lib/withStyles'; 124 import s from './Navigation.scss'; 125 126 function Navigation() { 127 return ( 128 <nav className={`${s.root} ${this.props.className}`}> 129 <ul className={s.items}> 130 <li className={`${s.item} ${s.selected}`}> 131 <a className={s.link} href="/products">Products</a> 132 </li> 133 <li className={s.item}> 134 <a className={s.link} href="/services">Services</a> 135 </li> 136 </ul> 137 </nav> 138 ); 139 } 140 141 Navigation.propTypes = { className: PropTypes.string }; 142 143 export default withStyles(Navigation, s); 144 ``` 145 146 ### Use higher-order components 147 148 * Use higher-order components (HOC) to extend existing React components.<br> 149 Here is an example: 150 151 ```js 152 // withViewport.js 153 import React, { Component } from 'react'; 154 import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; 155 156 function withViewport(ComposedComponent) { 157 return class WithViewport extends Component { 158 159 state = { 160 viewport: canUseDOM ? 161 {width: window.innerWidth, height: window.innerHeight} : 162 {width: 1366, height: 768} // Default size for server-side rendering 163 }; 164 165 componentDidMount() { 166 window.addEventListener('resize', this.handleResize); 167 window.addEventListener('orientationchange', this.handleResize); 168 } 169 170 componentWillUnmount() { 171 window.removeEventListener('resize', this.handleResize); 172 window.removeEventListener('orientationchange', this.handleResize); 173 } 174 175 handleResize = () => { 176 let viewport = {width: window.innerWidth, height: window.innerHeight}; 177 if (this.state.viewport.width !== viewport.width || 178 this.state.viewport.height !== viewport.height) { 179 this.setState({ viewport }); 180 } 181 }; 182 183 render() { 184 return <ComposedComponent {...this.props} viewport={this.state.viewport}/>; 185 } 186 187 }; 188 }; 189 190 export default withViewport; 191 ``` 192 193 ```js 194 // MyComponent.js 195 import React from 'react'; 196 import withViewport from './withViewport'; 197 198 class MyComponent { 199 render() { 200 let { width, height } = this.props.viewport; 201 return <div>{`Viewport: ${width}x${height}`}</div>; 202 } 203 } 204 205 export default withViewport(MyComponent); 206 ``` 207 208 **[⬆ back to top](#table-of-contents)**