└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # React / Redux Style Guide 2 | 3 | This is an opinionated style guide for developing applications in ES6+ with React and/or Redux. 4 | 5 | These styles are based on current best practices in the [React] and [Redux] communities, as well as real life experience 6 | with these tools in the field. It puts great focus on writing readable and maintainable code and using new JavaScript 7 | features in a responsible manner. 8 | 9 | [React]: https://facebook.github.io/react/ 10 | [Redux]: redux.js.org 11 | 12 | ## Credit 13 | 14 | Much of this style guide is based on the Redux documentation and ideas from [Dan Abramov] in particular. Furthermore, it 15 | was inspired by the [Angular Style Guide] by John Papa. 16 | 17 | [Dan Abramov]: https://github.com/gaearon 18 | [Angular Style Guide]: https://github.com/johnpapa/angular-styleguide 19 | 20 | ## Contributing 21 | 22 | This guide is opinionated. You'll probably disagree on some points. Feel free to [open an issue] or file a pull request 23 | if you think something should be rephrased or changed. Please include a detailed reasoning about why your way is better, 24 | preferably with links to other sources. I change my mind often (for the better), so please convince me. 25 | 26 | [open an issue]: https://github.com/ghengeveld/react-redux-styleguide/issues/new 27 | 28 | ## Table of Contents 29 | 30 | - [ES6 and beyond](#es6-and-beyond) 31 | - [Variables: var, let and const](#variables-var-let-and-const) 32 | - [Variables: declaration and usage](#variables-declaration-and-usage) 33 | - [Arrow functions](#arrow-functions) 34 | - [Pure functions](#pure-functions) 35 | - [Classes](#classes) 36 | - [React](#react) 37 | - [Components and containers](#components-and-containers) 38 | - [Dealing with props](#dealing-with-props) 39 | - [Redux](#redux) 40 | - [Action creators](#action-creators) 41 | - [Reducers](#reducers) 42 | - [Connecting React components](#connecting-react-components) 43 | - [Services](#services) 44 | - [Utils](#utils) 45 | 46 | ## ES6 and beyond 47 | 48 | Developers which have recently discovered the power of ES6 often feel compelled to use them on every occasion. This is 49 | not a very good idea as it can quickly lead to unreadable code. With great power comes great responsibility. 50 | 51 | ### Variables: var, let and const 52 | 53 | - Consider `var` deprecated. There is no use case for it anymore. 54 | - Use `const` by default. Immutable bindings are a good starting point. Code is easier to read and comprehend because 55 | you can be sure the variable is never re-assigned. 56 | - There's very few use cases for `let`. On many occasions you can use functions like map and reduce to avoid using it. 57 | 58 | ```js 59 | // deprecated: 60 | var foo = 'foo'; 61 | 62 | // avoid: 63 | let bar = 'bar'; 64 | 65 | // avoid: 66 | const foo = 'foo', 67 | bar = 'bar', 68 | baz = 'baz'; 69 | 70 | // recommended: 71 | const foo = 'foo'; 72 | const bar = 'bar'; 73 | const baz = 'baz'; 74 | ``` 75 | 76 | ### Variables: declaration and usage 77 | 78 | - Declare variables where you use them. 79 | - Declare each variable as a full statement. It avoids the dangling comma discussion. 80 | - Separate logical chunks of code with an empty line. 81 | 82 | ```js 83 | const foo = 'foo'; 84 | const bar = 'bar'; 85 | // use foo and bar 86 | 87 | const baz = 'baz'; 88 | // use baz 89 | 90 | const qux = 'qux'; 91 | // use qux 92 | ``` 93 | 94 | Common practice with `var` was to put all declarations at the top of their scope, even if they would only be used much 95 | further down. The reason for this is that `var` declarations are automatically hoisted to the top of their scope, so 96 | putting them there yourself makes the code more predictable. However, `let` and `const` are not hoisted. Therefore there 97 | is no reason to 'hoist' them yourself. Generally it's better to declare variables just-in-time, right before the spot 98 | where you are going to use them. This makes the code easier to follow. You won't have to go looking for usages before 99 | they are defined, because they can't be used there. It also makes it easier to extract a bunch of statements into a 100 | function, which is a great way to improve readability even more. 101 | 102 | ### Arrow functions 103 | 104 | - Consider anonymous functions deprecated. We have arrow functions for that now. 105 | - Use arrow functions only as part of a larger (named) entity, not as a stand-alone entity. 106 | - Declare functions after using them by leveraging function hoisting. 107 | 108 | ```js 109 | // deprecated 110 | function (string) { 111 | return string.toLocaleUpperCase(); 112 | } 113 | 114 | // recommended 115 | function upper(string) { 116 | return string.toLocaleUpperCase(); 117 | } 118 | 119 | // use wisely 120 | string => string.toLocaleUpperCase() 121 | 122 | // if it fits your style 123 | const upper = string => string.toLocaleUpperCase() 124 | ``` 125 | 126 | With the addition of the arrow function syntax, we have gained a very elegant and convenient way to define anonymous 127 | functions. Because they use 'lexical this', usage of `this` is more predictable. Therefore you should use arrows instead 128 | of regular anonymous functions. 129 | 130 | Unfortunately, it's tempting to forgo regular functions entirely and switch to arrows completely. Developers seem to 131 | think that with arrows, they never have to write that pesky `function` keyword again. However, a fully fledged function 132 | definition still has its merits. Arrow functions have various benefits, but also two major drawbacks: they are anonymous 133 | and they don't stand out in the code. 134 | 135 | The most important thing when writing readable code is to name things. Naming your functions is the best way to document 136 | what your code does. A well named function relieves the burden of having to read and comprehend the actual code. Another 137 | drawback of anonymous functions is that they don't encourage reuse. With arrows it's too easy to write a new function 138 | for each use case rather then reuse more generic functions. Finally, anonymous functions commonly aren't exported, which 139 | makes them hard to unit test. 140 | 141 | That being said, arrow functions certainly have their place. They are incredibly convenient as callback functions and 142 | when combined with array methods (map, reduce, forEach, filter, etc.) or other functional constructs they make for very 143 | elegant code. In the end it's up to you to find the right balance between elegance and documentation. Whenever you've 144 | written an arrow function, take a step back and consider if using a named function instead would improve readability. 145 | 146 | Like the Angular Style Guide we recommend the use of [function hoisting] in order to put your primary code at the top 147 | and implementation details at the bottom. Such code is much easier to read because it's in chronologic order and focuses 148 | on the bigger picture. If you've ever implemented this rule from the Angular Style Guide, you know this will let you 149 | grok the contents of a file much faster. 150 | 151 | You can of course assign your arrow function to a variable in order to 'name' it. Feel free to choose this approach if 152 | it fits your coding style, but consider your less functional-oriented colleagues. However, this makes regular variables 153 | and functions look extremely similar, making it harder to tell them apart. You will also lose the benefits of function 154 | hoisting. However there are also use cases where this style makes more sense, particularly if the function is a 155 | one-liner. Using a named function here would require at least three lines of code. 156 | 157 | [function hoisting]: https://github.com/johnpapa/angular-styleguide#function-declarations-to-hide-implementation-details 158 | 159 | ### Pure functions 160 | 161 | - Don't use shared mutable state or access variables from outer scope. 162 | - Don't cause any side effects by running a function. 163 | - Don't mutate any passed arguments, return a new value instead. 164 | 165 | ```js 166 | // avoid side-effects 167 | let x; 168 | function inc() { 169 | x++; 170 | } 171 | 172 | // recommended 173 | function inc(x) { 174 | return x + 1; 175 | } 176 | 177 | // avoid mutation 178 | function inc(object) { 179 | object.value++; 180 | } 181 | 182 | // recommended 183 | function inc(object) { 184 | return { ...object, value: object.value + 1 }; 185 | } 186 | ``` 187 | 188 | A sure way to reduce code complexity and enhance testability and readability is to keep your functions [pure]. This is 189 | one of the primary aspects of functional programming. It involves writing functions in a purely input-output fashion. 190 | Pure functions only work with the data they are given through their arguments and return some new data, without causing 191 | side effects. That means they cannot access outside scope and they cannot mutate their arguments. 192 | 193 | [pure]: https://en.wikipedia.org/wiki/Pure_function 194 | 195 | ### Classes 196 | 197 | - Only use classes if you have a very good reason for it. 198 | - Prefer composition over inheritance ("foo HAS a bar" instead of "foo IS a bar"). 199 | - Never inherit more than one level deep. 200 | - Never use `instanceof` checks, use duck typing instead. 201 | 202 | Classes have always been a hot topic of discussion in the JavaScript world. ES6 has introduced a new way to define 203 | classes, even though there is a growing tendency to avoid them. Programmers coming from an OOP background will 204 | appreciate them while the more functionally inclined don't like them. Developers working with React and Redux mostly 205 | prefer the functional approach, which is why using classes is discouraged. Downsides include: 206 | 207 | - Classes encourage mutation because they contain state. 208 | - Classes encourage coupling because they are used as contracts. 209 | - Classes encourage inheritance, but we want composition. 210 | - Instances can't be easily cloned without losing their type, making it hard to write pure functions. 211 | - Instance methods can't really be pure, it wouldn't make sense. 212 | - Properties are never really private. 213 | 214 | ## React 215 | 216 | ### Components and containers 217 | 218 | - Separate 'components' from 'containers'. 219 | - Prefer using a function for components, a class for containers. 220 | - Use PascalCase for component names. 221 | - Put each component in its own directory, inside index.js. 222 | - Expose components as the default export. 223 | - Expose secondary functions as named exports for unit testing. 224 | - Never export anonymous (arrow) functions. 225 | 226 | ```js 227 | // components/TodoItem/index.js 228 | export default function TodoItem(props, context) { 229 | return