├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.txt ├── READINGS.md ├── README.md ├── SUMMARY.md ├── anti-patterns ├── 01.props-in-initial-state.md ├── 02.findDOMNode.md ├── 03.mixins.md ├── 04.setState-in-componentWillMount.md ├── 05.mutating-state.md ├── 06.using-indexes-as-key.md ├── 07.spreading-props-dom.md └── README.md ├── book.json ├── coding-style └── README.md ├── gotchas ├── 01.pure-render-checks.md ├── 02.synthetic-events.md └── README.md ├── package.json ├── patterns ├── 1.conditionals-in-jsx.md ├── 10.passing-function-to-setState.md ├── 11.decorators.md ├── 12.feature-flags-using-redux.md ├── 13.component-switch.md ├── 14.reaching-into-a-component.md ├── 15.list-components.md ├── 16.format-text-via-component.md ├── 17.react-fragments.md ├── 18.share-tracking-logic.md ├── 2.async-nature-of-setState.md ├── 3.dependency-injection.md ├── 4.context-wrapper.md ├── 5.event-handlers.md ├── 6.flux-pattern.md ├── 7.one-way-data-flow.md ├── 8.presentational-vs-container.md └── 9.third-party-integration.md ├── perf-tips ├── 01.shouldComponentUpdate-check.md ├── 02.pure-component.md ├── 03.reselect.md └── README.md ├── styling ├── 01.stateless-ui-components.md ├── 02.styles-module.md ├── 03.style-functions.md ├── 04.using-npm-modules.md ├── 05.base-component.md ├── 06.layout-component.md ├── 07.typography-component.md ├── 08.HOC-for-styling.md └── README.md └── ux-variations ├── 01.composing-variations.md ├── 02.toggle-ui-elements.md ├── 03.HOC-feature-toggles.md ├── 04.HOC-props-proxy.md ├── 05.wrapper-components.md ├── 06.display-order-variations.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | _book 2 | node_modules 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | > Your contributions are heartily ♡ welcome. (✿◠‿◠) 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public licenses. 379 | Notwithstanding, Creative Commons may elect to apply one of its public 380 | licenses to material it publishes and in those instances will be 381 | considered the "Licensor." Except for the limited purpose of indicating 382 | that material is shared under a Creative Commons public license or as 383 | otherwise permitted by the Creative Commons policies published at 384 | creativecommons.org/policies, Creative Commons does not authorize the 385 | use of the trademark "Creative Commons" or any other trademark or logo 386 | of Creative Commons without its prior written consent including, 387 | without limitation, in connection with any unauthorized modifications 388 | to any of its public licenses or any other arrangements, 389 | understandings, or agreements concerning use of licensed material. For 390 | the avoidance of doubt, this paragraph does not form part of the public 391 | licenses. 392 | 393 | Creative Commons may be contacted at creativecommons.org. 394 | -------------------------------------------------------------------------------- /READINGS.md: -------------------------------------------------------------------------------- 1 | # Related Links 2 | - [React in Patterns by krasimir](https://github.com/krasimir/react-in-patterns) 3 | - [React Patterns by planningcenter](https://github.com/planningcenter/react-patterns) 4 | - [reactpatterns.com](https://github.com/chantastic/reactpatterns.com) 5 | - [10 React Mini-patterns](https://hackernoon.com/10-react-mini-patterns-c1da92f068c5) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [React Bits](https://vasanthk.gitbooks.io/react-bits) 2 | 3 | A compilation of React Patterns, techniques, tips and tricks. 4 | 5 | **Gitbook format**: https://vasanthk.gitbooks.io/react-bits 6 | 7 | **Github repo**: https://github.com/vasanthk/react-bits 8 | 9 | > Your contributions are heartily ♡ welcome. (✿◠‿◠) 10 | 11 | > **Translations by community:** 12 | > - 中文版 (Chinese): [react-bits-cn](https://github.com/hateonion/react-bits-CN) 13 | > - 한국어 (Korean): [react-bits-ko](https://github.com/rayleighko/react-bits-ko) 14 | 15 | 16 | - Design Patterns and Techniques 17 | - [Conditional in JSX](./patterns/1.conditionals-in-jsx.md) 18 | - [Async Nature Of setState()](./patterns/2.async-nature-of-setState.md) 19 | - [Dependency Injection](./patterns/3.dependency-injection.md) 20 | - [Context Wrapper](./patterns/4.context-wrapper.md) 21 | - [Event Handlers](./patterns/5.event-handlers.md) 22 | - [Flux Pattern](./patterns/6.flux-pattern.md) 23 | - [One Way Data Flow](./patterns/7.one-way-data-flow.md) 24 | - [Presentational vs Container](./patterns/8.presentational-vs-container.md) 25 | - [Third Party Integration](./patterns/9.third-party-integration.md) 26 | - [Passing Function To setState()](./patterns/10.passing-function-to-setState.md) 27 | - [Decorators](./patterns/11.decorators.md) 28 | - [Feature Flags](./patterns/12.feature-flags-using-redux.md) 29 | - [Component Switch](./patterns/13.component-switch.md) 30 | - [Reaching Into A Component](./patterns/14.reaching-into-a-component.md) 31 | - [List Components](./patterns/15.list-components.md) 32 | - [Format Text via Component](./patterns/16.format-text-via-component.md) 33 | - [React Fragments](./patterns/17.react-fragments.md) 34 | - [Share Tracking Logic](./patterns/18.share-tracking-logic.md) 35 | - Anti-Patterns 36 | - [Introduction](./anti-patterns/README.md) 37 | - [Props In Initial State](./anti-patterns/01.props-in-initial-state.md) 38 | - [findDOMNode()](./anti-patterns/02.findDOMNode.md) 39 | - [Mixins](./anti-patterns/03.mixins.md) 40 | - [setState() in componentWillMount()](./anti-patterns/04.setState-in-componentWillMount.md) 41 | - [Mutating State](./anti-patterns/05.mutating-state.md) 42 | - [Using Indexes as Key](./anti-patterns/06.using-indexes-as-key.md) 43 | - [Spreading Props on DOM elements](./anti-patterns/07.spreading-props-dom.md) 44 | - Handling UX Variations 45 | - [Introduction](./ux-variations/README.md) 46 | - [Composing UX Variations](./ux-variations/01.composing-variations.md) 47 | - [Toggle UI Elements](./ux-variations/02.toggle-ui-elements.md) 48 | - [HOC for Feature Toggles](./ux-variations/03.HOC-feature-toggles.md) 49 | - [HOC props proxy](./ux-variations/04.HOC-props-proxy.md) 50 | - [Wrapper Components](./ux-variations/05.wrapper-components.md) 51 | - [Display Order Variations](./ux-variations/06.display-order-variations.md) 52 | - Perf Tips 53 | - [Introduction](./perf-tips/README.md) 54 | - [shouldComponentUpdate() check](./perf-tips/01.shouldComponentUpdate-check.md) 55 | - [Using Pure Components](./perf-tips/02.pure-component.md) 56 | - [Using reselect](./perf-tips/03.reselect.md) 57 | - Styling 58 | - [Introduction](./styling/README.md) 59 | - [Stateless UI Components](./styling/01.stateless-ui-components.md) 60 | - [Styles Module](./styling/02.styles-module.md) 61 | - [Style Functions](./styling/03.style-functions.md) 62 | - [NPM Modules](./styling/04.using-npm-modules.md) 63 | - [Base Component](./styling/05.base-component.md) 64 | - [Layout Component](./styling/06.layout-component.md) 65 | - [Typography Component](./styling/07.typography-component.md) 66 | - [HOC for Styling](./styling/08.HOC-for-styling.md) 67 | - Gotchas 68 | - [Introduction](./gotchas/README.md) 69 | - [Pure render checks](./gotchas/01.pure-render-checks.md) 70 | - [Synthetic Events](./gotchas/02.synthetic-events.md) 71 | - [Related Links](./READINGS.md) 72 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # React Bits 2 | ✨ React patterns, techniques, tips and tricks ✨ 3 | 4 | - Design Patterns and Techniques 5 | - [Conditional in JSX](./patterns/18.conditionals-in-jsx.md) 6 | - [Async Nature Of setState()](./patterns/19.async-nature-of-setState.md) 7 | - [Dependency Injection](./patterns/20.dependency-injection.md) 8 | - [Context Wrapper](./patterns/21.context-wrapper.md) 9 | - [Event Handlers](./patterns/22.event-handlers.md) 10 | - [Flux Pattern](./patterns/23.flux-pattern.md) 11 | - [One Way Data Flow](./patterns/24.one-way-data-flow.md) 12 | - [Presentational vs Container](./patterns/25.presentational-vs-container.md) 13 | - [Third Party Integration](./patterns/26.third-party-integration.md) 14 | - [Passing Function To setState()](./patterns/27.passing-function-to-setState.md) 15 | - [Decorators](./patterns/28.decorators.md) 16 | - [Feature Flags](./patterns/29.feature-flags-using-redux.md) 17 | - [Component Switch](./patterns/30.component-switch.md) 18 | - [Reaching Into A Component](./patterns/31.reaching-into-a-component.md) 19 | - [List Components](./patterns/32.list-components.md) 20 | - [Format Text via Component](./patterns/33.format-text-via-component.md) 21 | - [Share Tracking Logic](./patterns/34.share-tracking-logic.md) 22 | - Anti-Patterns 23 | - [Introduction](./anti-patterns/README.md) 24 | - [Props In Initial State](./anti-patterns/01.props-in-initial-state.md) 25 | - [findDOMNode()](./anti-patterns/02.findDOMNode.md) 26 | - [Mixins](./anti-patterns/03.mixins.md) 27 | - [setState() in componentWillMount()](./anti-patterns/04.setState-in-componentWillMount.md) 28 | - [Mutating State](./anti-patterns/05.mutating-state.md) 29 | - [Using Indexes as Key](./anti-patterns/06.using-indexes-as-key.md) 30 | - [Spreading Props on DOM elements](./anti-patterns/07.spreading-props-dom.md) 31 | - Handling UX Variations 32 | - [Introduction](./ux-variations/README.md) 33 | - [Composing UX Variations](./ux-variations/01.composing-variations.md) 34 | - [Toggle UI Elements](./ux-variations/02.toggle-ui-elements.md) 35 | - [HOC for Feature Toggles](./ux-variations/03.HOC-feature-toggles.md) 36 | - [HOC props proxy](./ux-variations/04.HOC-props-proxy.md) 37 | - [Wrapper Components](./ux-variations/05.wrapper-components.md) 38 | - [Display Order Variations](./ux-variations/06.display-order-variations.md) 39 | - Perf Tips 40 | - [Introduction](./perf-tips/README.md) 41 | - [shouldComponentUpdate() check](./perf-tips/01.shouldComponentUpdate-check.md) 42 | - [Using Pure Components](./perf-tips/02.pure-component.md) 43 | - [Using reselect](./perf-tips/03.reselect.md) 44 | - Styling 45 | - [Introduction](./styling/README.md) 46 | - [Stateless UI Components](./styling/01.stateless-ui-components.md) 47 | - [Styles Module](./styling/02.styles-module.md) 48 | - [Style Functions](./styling/03.style-functions.md) 49 | - [npm Modules](./styling/04.using-npm-modules.md) 50 | - [Base Component](./styling/05.base-component.md) 51 | - [Layout Component](./styling/06.layout-component.md) 52 | - [Typography Component](./styling/07.typography-component.md) 53 | - [HOC for Styling](./styling/08.HOC-for-styling.md) 54 | - Gotchas 55 | - [Introduction](./gotchas/README.md) 56 | - [Pure render checks](./gotchas/01.pure-render-checks.md) 57 | - [Synthetic Events](./gotchas/02.synthetic-events.md) 58 | - [Related Links](./READINGS.md) 59 | -------------------------------------------------------------------------------- /anti-patterns/01.props-in-initial-state.md: -------------------------------------------------------------------------------- 1 | # Props in Initial State 2 | From docs: 3 | > Using props to generate state in getInitialState often leads to duplication of “source of truth”, i.e. where the real data is. 4 | > This is because getInitialState is only invoked when the component is first created. 5 | 6 | The danger is that if the props on the component are changed without the component being ‘refreshed’, 7 | the new prop value will never be displayed because the constructor function (or getInitialState) will never update the current state of the component. 8 | The initialization of state from props only runs when the component is first created. 9 | 10 | #### Bad 11 | ```javascript 12 | class SampleComponent extends Component { 13 | // constructor function (or getInitialState) 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | flag: false, 18 | inputVal: props.inputValue 19 | }; 20 | } 21 | 22 | render() { 23 | return
{this.state.inputVal && }
24 | } 25 | } 26 | ``` 27 | #### Good 28 | ```javascript 29 | class SampleComponent extends Component { 30 | // constructor function (or getInitialState) 31 | constructor(props) { 32 | super(props); 33 | this.state = { 34 | flag: false 35 | }; 36 | } 37 | 38 | render() { 39 | return
{this.props.inputValue && }
40 | } 41 | } 42 | ``` 43 | 44 | ### Related links: 45 | - [React Anti-Patterns: Props in Initial State](https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e) 46 | - [Why is passing the component initial state a prop an anti-pattern?](http://stackoverflow.com/questions/28785106/reactjs-why-is-passing-the-component-initial-state-a-prop-an-anti-pattern) 47 | -------------------------------------------------------------------------------- /anti-patterns/02.findDOMNode.md: -------------------------------------------------------------------------------- 1 | # Refs over findDOMNode() 2 | Use callback refs over findDOMNode() 3 | 4 | Note: 5 | React also supports using a string (instead of a callback) as a ref prop on any component, although this approach is mostly legacy at this point. 6 | - [More about refs](https://reactjs.org/docs/refs-and-the-dom.html) 7 | - [Why ref-string is legacy?](http://stackoverflow.com/questions/37468913/why-ref-string-is-legacy) 8 | 9 | ##### findDOMNode(this) 10 | 11 | ###### Before: 12 | ```javascript 13 | class MyComponent extends Component { 14 | componentDidMount() { 15 | findDOMNode(this).scrollIntoView(); 16 | } 17 | 18 | render() { 19 | return
20 | } 21 | } 22 | ``` 23 | ###### After 24 | ```javascript 25 | class MyComponent extends Component { 26 | componentDidMount() { 27 | this.node.scrollIntoView(); 28 | } 29 | 30 | render() { 31 | return
this.node = node}/> 32 | } 33 | } 34 | ``` 35 | ##### findDOMNode(stringDOMRef) 36 | ###### Before 37 | ```javascript 38 | class MyComponent extends Component { 39 | componentDidMount() { 40 | findDOMNode(this.refs.something).scrollIntoView(); 41 | } 42 | 43 | render() { 44 | return ( 45 |
46 |
47 |
48 | ) 49 | } 50 | } 51 | ``` 52 | ###### After 53 | ```javascript 54 | class MyComponent extends Component { 55 | componentDidMount() { 56 | this.something.scrollIntoView(); 57 | } 58 | 59 | render() { 60 | return ( 61 |
62 |
this.something = node}/> 63 |
64 | ) 65 | } 66 | } 67 | ``` 68 | ##### findDOMNode(childComponentStringRef) 69 | ###### Before: 70 | ```javascript 71 | class Field extends Component { 72 | render() { 73 | return 74 | } 75 | } 76 | 77 | class MyComponent extends Component { 78 | componentDidMount() { 79 | findDOMNode(this.refs.myInput).focus(); 80 | } 81 | 82 | render() { 83 | return ( 84 |
85 | Hello, 86 | 87 |
88 | ) 89 | } 90 | } 91 | ``` 92 | ###### After 93 | ```javascript 94 | class Field extends Component { 95 | render() { 96 | return ( 97 | 98 | ) 99 | } 100 | } 101 | 102 | class MyComponent extends Component { 103 | componentDidMount() { 104 | this.inputNode.focus(); 105 | } 106 | 107 | render() { 108 | return ( 109 |
110 | Hello, 111 | this.inputNode = node}/> 112 |
113 | ) 114 | } 115 | } 116 | ``` 117 | 118 | ### Related links: 119 | - [ESLint Rule proposal: warn against using findDOMNode()](https://github.com/yannickcr/eslint-plugin-react/issues/678#issue-165177220) 120 | - [Refs and the DOM](https://reactjs.org/docs/refs-and-the-dom.html) 121 | -------------------------------------------------------------------------------- /anti-patterns/03.mixins.md: -------------------------------------------------------------------------------- 1 | # Use Higher order components over Mixins 2 | 3 | #### Simple Example 4 | ```javascript 5 | // With Mixin 6 | var WithLink = React.createClass({ 7 | mixins: [React.addons.LinkedStateMixin], 8 | getInitialState: function () { 9 | return {message: 'Hello!'}; 10 | }, 11 | render: function () { 12 | return ; 13 | } 14 | }); 15 | 16 | // Move logic to a HOC 17 | var WithLink = React.createClass({ 18 | getInitialState: function () { 19 | return {message: 'Hello!'}; 20 | }, 21 | render: function () { 22 | return ; 23 | } 24 | }); 25 | ``` 26 | 27 | #### Detailed Example 28 | 29 | ```javascript 30 | // With Mixin 31 | var CarDataMixin = { 32 | componentDidMount: { 33 | // fetch car data and 34 | // call this.setState({carData: fetchedData}), 35 | // once data has been (asynchronously) fetched 36 | } 37 | }; 38 | 39 | var FirstView = React.createClass({ 40 | mixins: [CarDataMixin], 41 | render: function () { 42 | return ( 43 |
44 | 45 | 46 | 47 |
48 | ) 49 | } 50 | }); 51 | 52 | // With HOC 53 | var bindToCarData = function (Component) { 54 | return React.createClass({ 55 | componentDidMount: { 56 | // fetch car data and 57 | // call this.setState({carData: fetchedData}), 58 | // once data has been (asynchronously) fetched 59 | }, 60 | 61 | render: function () { 62 | return 63 | } 64 | }); 65 | }; 66 | 67 | // Then wrap your component when you define it. 68 | var FirstView = bindToCarData(React.createClass({ 69 | render: function () { 70 | return ( 71 |
72 | 73 | 74 | 75 |
76 | ) 77 | } 78 | })); 79 | ``` 80 | 81 | ### Related links: 82 | - [Mixins are dead - Long live higher ordercomponents](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750) 83 | - [Mixins are considered harmful](https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html) 84 | - [Stackoverflow: Using mixins vs components for code reuse](http://stackoverflow.com/questions/21854938/using-mixins-vs-components-for-code-reuse-in-facebook-react) 85 | - [Stackoverflow: Composition instead of mixins in React](http://stackoverflow.com/questions/30845561/how-to-solve-this-using-composition-instead-of-mixins-in-react) 86 | -------------------------------------------------------------------------------- /anti-patterns/04.setState-in-componentWillMount.md: -------------------------------------------------------------------------------- 1 | # setState() in componentWillMount() 2 | Avoid async initialization in ``componentWillMount()`` 3 | 4 | ``componentWillMount()`` is invoked immediately before mounting occurs. 5 | It is called before ``render()``, therefore setting state in this method will not trigger a re-render. 6 | Avoid introducing any side-effects or subscriptions in this method. 7 | 8 | 9 | Make async calls for component initialization in ``componentDidMount`` instead of ``componentWillMount`` 10 | ```javascript 11 | function componentDidMount() { 12 | axios.get(`api/messages`) 13 | .then((result) => { 14 | const messages = result.data 15 | console.log("COMPONENT WILL Mount messages : ", messages); 16 | this.setState({ 17 | messages: [...messages.content] 18 | }) 19 | }) 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /anti-patterns/05.mutating-state.md: -------------------------------------------------------------------------------- 1 | # Mutating State without setState() 2 | - Causes state changes without making component re-render. 3 | - Whenever setState gets called in future, the mutated state gets applied. 4 | 5 | #### Bad 6 | ``` javascript 7 | class SampleComponent extends Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | this.state = { 12 | items: ['foo', 'bar'] 13 | }; 14 | 15 | this.handleClick = this.handleClick.bind(this); 16 | } 17 | 18 | handleClick() { 19 | // BAD: We mutate state here 20 | this.state.items.push('lorem'); 21 | 22 | this.setState({ 23 | items: this.state.items 24 | }); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | {this.state.items.length} 31 | 32 |
33 | ) 34 | } 35 | } 36 | ``` 37 | 38 | #### Good 39 | ``` javascript 40 | class SampleComponent extends Component { 41 | constructor(props) { 42 | super(props); 43 | 44 | this.state = { 45 | items: ['foo', 'bar'] 46 | }; 47 | 48 | this.handleClick = this.handleClick.bind(this); 49 | } 50 | 51 | handleClick() { 52 | // We update using setState() - concat return new array after appending new item. 53 | this.setState(prevState => ({ 54 | items: prevState.items.concat('lorem') 55 | })); 56 | } 57 | 58 | render() { 59 | return ( 60 |
61 | {this.state.items.length} 62 | 63 |
64 | ) 65 | } 66 | } 67 | ``` 68 | 69 | ### Related links: 70 | [React Design Patterns and best practices by Michele Bertoli.](https://github.com/MicheleBertoli/react-design-patterns-and-best-practices) 71 | -------------------------------------------------------------------------------- /anti-patterns/06.using-indexes-as-key.md: -------------------------------------------------------------------------------- 1 | # Using indexes as keys 2 | Keys should be stable, predictable, and unique so that React can keep track of elements. 3 | 4 | #### Bad 5 | In this snippet each element's key will be based on ordering, rather than tied to the data that is being represented. This limits the optimizations that React can do. 6 | ```javascript 7 | {todos.map((todo, index) => 8 | 12 | )} 13 | ``` 14 | 15 | #### Good 16 | Assuming `todo.id` is unique to this list and stable, React would be able to reorder elements without needing to reevaluate them as much. 17 | ```javascript 18 | {todos.map((todo) => 19 | 21 | )} 22 | ``` 23 | 24 | ### @Reference: 25 | - [React docs](https://reactjs.org/docs/reconciliation.html#tradeoffs) 26 | - [Lin Clark's code cartoon](https://youtu.be/-t8eOoRsJ7M?t=981) 27 | -------------------------------------------------------------------------------- /anti-patterns/07.spreading-props-dom.md: -------------------------------------------------------------------------------- 1 | # Spreading props on DOM elements 2 | When we spread props we run into the risk of adding unknown HTML attributes, which is a bad practice. 3 | 4 | #### Bad 5 | This will try to add the unknown HTML attribute `flag` to the DOM element. 6 | ```javascript 7 | const Sample = () => (); 8 | const Spread = (props) => (
Test
); 9 | ``` 10 | #### Good 11 | By creating props specifically for DOM attribute, we can safely spread. 12 | ```javascript 13 | const Sample = () => (); 14 | const Spread = (props) => (
Test
); 15 | ``` 16 | 17 | Or alternatively we can use prop destructuring with `...rest`: 18 | ```javascript 19 | const Sample = () => (); 20 | const Spread = ({ flag, ...domProps }) => (
Test
); 21 | ``` 22 | 23 | *Note* 24 | 25 | In [scenarios](https://github.com/vasanthk/react-bits/issues/34) where you use a [PureComponent](../perf-tips/02.pure-component.md), when an update happens it re-renders the component even if `domProps` did not change. This is because PureComponent only [shallowly compares](https://reactjs.org/docs/react-api.html#reactpurecomponent) the objects. 26 | 27 | ### Related links: 28 | - [React Design Patterns and best practices by Michele Bertoli.](https://github.com/MicheleBertoli/react-design-patterns-and-best-practices) 29 | - [In React, children are just props: Kent C. Dodds' Tweet](https://twitter.com/kentcdodds/status/851406788549369856) 30 | -------------------------------------------------------------------------------- /anti-patterns/README.md: -------------------------------------------------------------------------------- 1 | # Anti-patterns 2 | 3 | Familiarizing ourselves with common anti-patterns will help us understand how React works and describe useful forms of refactoring our code. 4 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">3.0.0", 3 | "plugins": ["edit-link"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vasanthk/react-bits/blob/master", 7 | "label": "Edit This Page" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vasanthk/react-bits/" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /coding-style/README.md: -------------------------------------------------------------------------------- 1 | # Coding Style 2 | 3 | **Disclaimer:** 4 | 5 | This section is purely to familiarize oneself with different coding styles out there. It is not a set of coding standards or conventions! 6 | -------------------------------------------------------------------------------- /gotchas/01.pure-render-checks.md: -------------------------------------------------------------------------------- 1 | # Pure Render Checks 2 | 3 | In order to preserve performance one needs to consider the creation of new entities in the render method. 4 | 5 | Pure render? 6 | 7 | With React.js pure render I mean components that implement the shouldComponentUpdate method with shallow equality checks. 8 | 9 | Examples of this are the React.PureComponent, PureRenderMixin, recompose/pure and many others. 10 | 11 | #### Case 1 12 | 13 | ##### Bad 14 | ```javascript 15 | class Table extends PureComponent { 16 | render() { 17 | return ( 18 |
19 | {this.props.items.map(i => 20 | 21 | )} 22 |
23 | ); 24 | } 25 | } 26 | ``` 27 | 28 | **The issue is with ``{this.props.options || []}`` - it caused all the cells to be re-rendered even for a single cell change. Why?** 29 | 30 | You see the options array was passed deep down in the Cell elements. Normally this would not be an issue. 31 | The other Cell elements would not be re-rendered because they can do the cheap shallow equality check and 32 | skip the render entirely but in this case the options prop was null and the default array was used. 33 | As you should know the array literal is the same as new Array() which creates a new array instance. 34 | This completely destroyed every pure render optimization inside the Cell elements. 35 | In Javascript different instances have different identities and thus the shallow equality check always 36 | produces false and tells React to re-render the components. 37 | 38 | ##### Good 39 | ```javascript 40 | const defaultval = []; // <--- The fix (defaultProps could also have been used). 41 | class Table extends PureComponent { 42 | render() { 43 | return ( 44 |
45 | {this.props.items.map(i => 46 | 47 | )} 48 |
49 | ); 50 | } 51 | } 52 | ``` 53 | #### Case 2 54 | Similar issue with using functions in render() as well 55 | 56 | ##### BAD 57 | ```javascript 58 | class App extends PureComponent { 59 | render() { 60 | return this.props.update(e.target.value)}/>; 62 | } 63 | } 64 | ``` 65 | ##### Bad again 66 | ```javascript 67 | class App extends PureComponent { 68 | update(e) { 69 | this.props.update(e.target.value); 70 | } 71 | 72 | render() { 73 | return ; 74 | } 75 | } 76 | ``` 77 | ^^In both cases a new function is created with a new identity. Just like with the array literal. 78 | We need to bind the function early 79 | 80 | ##### Good 81 | ```javascript 82 | class App extends PureComponent { 83 | constructor(props) { 84 | super(props); 85 | this.update = this.update.bind(this); 86 | } 87 | 88 | update(e) { 89 | this.props.update(e.target.value); 90 | } 91 | 92 | render() { 93 | return ; 94 | } 95 | } 96 | ``` 97 | ##### Bad 98 | ```javascript 99 | class Component extends React.Component { 100 | state = {clicked: false}; 101 | 102 | onClick() { 103 | this.setState({clicked: true}) 104 | } 105 | 106 | render() { 107 | // Options object created each render if not set 108 | const options = this.props.options || {test: 1}; 109 | 110 | return this.onClick(event) 116 | /> 117 | } 118 | } 119 | ``` 120 | ##### Good 121 | ```javascript 122 | class Component extends React.Component { 123 | state = {clicked: false}; 124 | options = {test: 1}; 125 | 126 | onClick = () => { 127 | this.setState({clicked: true}) 128 | }; 129 | 130 | render() { 131 | // Options object created once 132 | const options = this.props.options || this.options; 133 | 134 | return 139 | } 140 | } 141 | ``` 142 | 143 | ### Related links: 144 | - https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f 145 | - https://github.com/nfour/js-structures/blob/master/guides/react-anti-patterns.md#pure-render-immutability 146 | - [Optimizing React Rendering](https://flexport.engineering/optimizing-react-rendering-part-1-9634469dca02) 147 | -------------------------------------------------------------------------------- /gotchas/02.synthetic-events.md: -------------------------------------------------------------------------------- 1 | # Synthetic events in React 2 | Inside React event handlers, the event object is wrapped in a SyntheticEvent object. 3 | These objects are pooled, which means that the objects received at an event handler will be reused for other events to increase performance. 4 | This also means that accessing the event object’s properties asynchronously will be impossible since the event’s properties have been reset due to reuse. 5 | 6 | The following piece of code will log null because event has been reused inside the SyntheticEvent pool: 7 | ```javascript 8 | function handleClick(event) { 9 | setTimeout(function () { 10 | console.log(event.target.name); 11 | }, 1000); 12 | } 13 | ``` 14 | To avoid this you need to store the event’s property you are interested in inside its own binding: 15 | ```javascript 16 | function handleClick(event) { 17 | let name = event.target.name; 18 | setTimeout(function () { 19 | console.log(name); 20 | }, 1000); 21 | } 22 | ``` 23 | 24 | ### Related links: 25 | - [React/Redux: Best practices & gotchas](https://medium.com/nick-parsons/react-redux-best-practices-gotchas-56cf61c1c415) 26 | - [React events in depth w/ Kent C. Dodds, Ben Alpert, & Dan Abramov](https://www.youtube.com/watch?v=dRo_egw7tBc) -------------------------------------------------------------------------------- /gotchas/README.md: -------------------------------------------------------------------------------- 1 | # Gotchas 2 | 3 | React is intuitive for the most part, but there are quite a few stumbling points which might catch you by surprise. We'll discuss more on them here. 4 | 5 | ## Articles 6 | 7 | [React Gotchas](https://daveceddia.com/react-gotchas/) 8 | 9 | [Top 5 React Gotchas](http://joelgriffith.net/top-5-react-gotchas/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-bits", 3 | "version": "1.0.0", 4 | "repository": "https://github.com/vasanthk/react-bits.git", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "docs": "gitbook serve" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/1.conditionals-in-jsx.md: -------------------------------------------------------------------------------- 1 | # Conditionals in JSX 2 | 3 | Instead of 4 | ```javascript 5 | const sampleComponent = () => { 6 | return isTrue ?

True!

: null 7 | }; 8 | ``` 9 | 10 | Use short-circuit evaluation 11 | ```javascript 12 | const sampleComponent = () => { 13 | return isTrue &&

True!

14 | }; 15 | ``` 16 | For Complex scenarios with too many ternaries: 17 | 18 | ```javascript 19 | // Y soo many ternary??? :-/ 20 | const sampleComponent = () => { 21 | return ( 22 |
23 | {flag && flag2 && !flag3 24 | ? flag4 25 | ?

Blah

26 | : flag5 27 | ?

Meh

28 | :

Herp

29 | :

Derp

30 | } 31 |
32 | ) 33 | }; 34 | ``` 35 | 36 | - Best approach: Move logic to sub-components 37 | - Alternate hacky approach: Use IIFE 38 | 39 | There are some libraries that solve this problem (JSX-Control Statements), but rather than introduce another dependency 40 | use an [IIFE](http://stackoverflow.com/questions/8228281/what-is-the-function-construct-in-javascript) and return values by using if-else statement inside 41 | 42 | ```javascript 43 | const sampleComponent = () => { 44 | return ( 45 |
46 | { 47 | (() => { 48 | if (flag && flag2 && !flag3) { 49 | if (flag4) { 50 | return

Blah

51 | } else if (flag5) { 52 | return

Meh

53 | } else { 54 | return

Herp

55 | } 56 | } else { 57 | return

Derp

58 | } 59 | })() 60 | } 61 |
62 | ) 63 | }; 64 | ``` 65 | 66 | 67 | With an appropiate [transpiler](https://babeljs.io/docs/plugins/transform-do-expressions/) you can take advantage of the upcoming [do expression](https://github.com/tc39/proposal-do-expressions) which is currently on [stage-1](https://github.com/tc39/proposal-do-expressions) 68 | 69 | 70 | ```javascript 71 | const sampleComponent = () => { 72 | return ( 73 |
74 | { 75 | do => { 76 | if (flag && flag2 && !flag3) { 77 | if (flag4) { 78 |

Blah

79 | } else if (flag5) { 80 |

Meh

81 | } else { 82 |

Herp

83 | } 84 | } else { 85 |

Derp

86 | } 87 | } 88 | } 89 |
90 | ) 91 | }; 92 | ``` 93 | 94 | 95 | Or alternatively simply use early returns 96 | 97 | ```javascript 98 | const sampleComponent = () => { 99 | const basicCondition = flag && flag2 && !flag3; 100 | if (!basicCondition) return

Derp

; 101 | if (flag4) return

Blah

; 102 | if (flag5) return

Meh

; 103 | return

Herp

104 | } 105 | ``` 106 | 107 | ### Related links: 108 | - https://engineering.musefind.com/our-best-practices-for-writing-react-components-dec3eb5c3fc8 109 | - [Conditional rendering](https://reactjs.org/docs/conditional-rendering.html) 110 | 111 | 112 | -------------------------------------------------------------------------------- /patterns/10.passing-function-to-setState.md: -------------------------------------------------------------------------------- 1 | # Passing a function to setState 2 | 3 | Here’s the dirty secret about setState — it’s actually asynchronous. 4 | React batches state changes for performance reasons, so the state may not change immediately after setState is called. 5 | That means you should not rely on the current state when calling setState — since you can’t be sure what that state will be! 6 | Here’s the solution — pass a function to setState, with the previous state as an argument. 7 | Doing so avoids issues with the user getting the old state value on access (due to the asynchrony of setState) 8 | 9 | #### Problem 10 | ```javascript 11 | // assuming this.state.count === 0 12 | this.setState({count: this.state.count + 1}); 13 | this.setState({count: this.state.count + 1}); 14 | this.setState({count: this.state.count + 1}); 15 | // this.state.count === 1, not 3 16 | ``` 17 | #### Solution 18 | ```javascript 19 | this.setState((prevState, props) => ({ 20 | count: prevState.count + props.increment 21 | })); 22 | ``` 23 | 24 | #### Variations 25 | ```javascript 26 | // Passing object 27 | this.setState({ expanded: !this.state.expanded }); 28 | 29 | // Passing function 30 | this.setState(prevState => ({ expanded: !prevState.expanded })); 31 | ``` 32 | 33 | ### Related links: 34 | - [setState() Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82) 35 | - [Do I need to use setState(function) overload in this case?](http://stackoverflow.com/questions/43428456/do-i-need-to-use-setstatefunction-overload-in-this-case/43440790#43440790) 36 | - [Functional setState is the future of React](https://medium.freecodecamp.com/functional-setstate-is-the-future-of-react-374f30401b6b) 37 | -------------------------------------------------------------------------------- /patterns/11.decorators.md: -------------------------------------------------------------------------------- 1 | # Decorators 2 | Decorators (*supported by Babel, in Stage 2 proposal as of 03/17*) 3 | 4 | If you’re using something like mobx, you can decorate your class components — which is the same as passing the component into a function. 5 | Decorators are flexible and readable way of modifying component functionality. 6 | 7 | Non-decorators approach 8 | ```javascript 9 | class ProfileContainer extends Component { 10 | // Component code 11 | } 12 | export default observer(ProfileContainer) 13 | ``` 14 | With decorators 15 | ```javascript 16 | @observer 17 | export default class ProfileContainer extends Component { 18 | // Component code 19 | } 20 | ``` 21 | 22 | ### Article: 23 | - [Enhancing React components with Decorators](https://medium.com/@gigobyte/enhancing-react-components-with-decorators-441320e8606a) 24 | 25 | ### Related: 26 | - [Decorators != higher ordered components](https://twitter.com/dan_abramov/status/628202050946514944) 27 | - [React Decorator example - Module](https://github.com/gigobyte/react-document-title-decorator) 28 | - [What is the use of Connect(decorator in react-redux)](http://stackoverflow.com/questions/36553814/what-is-the-use-of-connect-decorator-in-react-redux) 29 | - [Decorators with React Components](http://stackoverflow.com/questions/36286384/decorators-with-react-components) 30 | - [Exploring ES7 decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.8cbzw5wcl) 31 | - [Understanding Decorators](https://survivejs.com/react/appendices/understanding-decorators/) 32 | -------------------------------------------------------------------------------- /patterns/12.feature-flags-using-redux.md: -------------------------------------------------------------------------------- 1 | # Feature Flags 2 | Enabling Feature flags in React using Redux 3 | 4 | ```javascript 5 | // createFeatureFlaggedContainer.js 6 | import React from 'react'; 7 | import { connect } from 'react-redux'; 8 | import { isFeatureEnabled } from './reducers' 9 | 10 | export default function createFeatureFlaggedContainer({ 11 | featureName, 12 | enabledComponent, 13 | disabledComponent 14 | }) { 15 | function FeatureFlaggedContainer({ isEnabled, ...props }) { 16 | const Component = isEnabled ? enabledComponent : disabledComponent; 17 | 18 | if (Component) { 19 | return ; 20 | } 21 | 22 | // `disabledComponent` is optional property 23 | return null; 24 | } 25 | 26 | // Having `displayName` is very useful for debugging. 27 | FeatureFlaggedContainer.displayName = `FeatureFlaggedContainer(${ featureName })`; 28 | 29 | return connect((store) => { 30 | isEnabled: isFeatureEnabled(store, featureName) 31 | })(FeatureFlaggedContainer); 32 | } 33 | ``` 34 | ```javascript 35 | // EnabledFeature.js 36 | import { connect } from 'react-redux'; 37 | import { isFeatureEnabled } from './reducers' 38 | 39 | function EnabledFeature({ isEnabled, children }) { 40 | if (isEnabled) { 41 | return children; 42 | } 43 | 44 | return null; 45 | } 46 | 47 | export default connect((store, { name }) => { 48 | isEnabled: isFeatureEnabled(store, name) 49 | })(EnabledFeature); 50 | ``` 51 | ```javascript 52 | // featureEnabled.js 53 | import createFeatureFlaggedContainer from './createFeatureFlaggedContainer' 54 | 55 | // Decorator for "Page" components. 56 | // usage: enabledFeature('unicorns')(UnicornsPage); 57 | export default function enabledFeature(featureName) { 58 | return (Component) => { 59 | return createFeatureFlaggedContainer({ 60 | featureName, 61 | enabledComponent: Component, 62 | disabledComponent: PageNotFound, // 404 page or something similar 63 | }); 64 | }; 65 | }; 66 | ``` 67 | ```javascript 68 | // features.js 69 | // This is quite simple reducer, containing only an array of features. 70 | // You can attach this data to a `currentUser` or similar reducer. 71 | 72 | // `BOOTSTAP` is global action, which contains the initial data for a page 73 | // Features access usually don't change during user usage of a page 74 | const BOOTSTAP = 'features/receive'; 75 | 76 | export default function featuresReducer(state, { type, payload }) { 77 | if (type === BOOTSTAP) { 78 | return payload.features || []; 79 | } 80 | 81 | return state || []; 82 | } 83 | 84 | export function isFeatureEnabled(features, featureName) { 85 | return features.indexOf(featureName) !== -1; 86 | } 87 | ``` 88 | ```javascript 89 | // reducers.js 90 | // This is your main reducer.js file 91 | import { combineReducers } from 'redux'; 92 | 93 | import features, { isFeatureEnabled as isFeatureEnabledSelector } from './features'; 94 | // ...other reducers 95 | 96 | export default combineReducers({ 97 | features 98 | // ...other reducers 99 | }); 100 | 101 | // This is the important part, access to `features` reducer should only happens 102 | // via this selector. 103 | // Then you can always change where/how the features are stored. 104 | export function isFeatureEnabled({ features }, featureName) { 105 | return isFeatureEnabledSelector(features, featureName); 106 | } 107 | ``` 108 | ### Related links: 109 | - [Feature flags in React](http://blog.rstankov.com/feature-flags-in-react/) 110 | - [Gist](https://gist.github.com/RStankov/0e764f27daf38f2fcd81b82360334528) 111 | -------------------------------------------------------------------------------- /patterns/13.component-switch.md: -------------------------------------------------------------------------------- 1 | # The switching component 2 | A switching component is a component that renders one of many components. 3 | 4 | Use an object to map prop values to components 5 | ```javascript 6 | import HomePage from './HomePage.jsx'; 7 | import AboutPage from './AboutPage.jsx'; 8 | import UserPage from './UserPage.jsx'; 9 | import FourOhFourPage from './FourOhFourPage.jsx'; 10 | 11 | const PAGES = { 12 | home: HomePage, 13 | about: AboutPage, 14 | user: UserPage 15 | }; 16 | 17 | const Page = (props) => { 18 | const Handler = PAGES[props.page] || FourOhFourPage; 19 | 20 | return 21 | }; 22 | 23 | // The keys of the PAGES object can be used in the prop types to catch dev-time errors. 24 | Page.propTypes = { 25 | page: PropTypes.oneOf(Object.keys(PAGES)).isRequired 26 | }; 27 | ``` 28 | 29 | ### Related links: 30 | - https://hackernoon.com/10-react-mini-patterns-c1da92f068c5 31 | -------------------------------------------------------------------------------- /patterns/14.reaching-into-a-component.md: -------------------------------------------------------------------------------- 1 | # Reaching into a Component 2 | Accessing a component from the parent. 3 | eg. Autofocus an input (controlled by parent component) 4 | 5 | #### Child Component 6 | An input component with a focus() method that focuses the HTML element 7 | ```javascript 8 | class Input extends Component { 9 | focus() { 10 | this.el.focus(); 11 | } 12 | 13 | render() { 14 | return ( 15 | { this.el = el; }} 17 | /> 18 | ); 19 | } 20 | } 21 | ``` 22 | #### Parent Component 23 | In the parent component, we can get a reference to the Input component and call its focus() method. 24 | ```javascript 25 | class SignInModal extends Component { 26 | componentDidMount() { 27 | // Note that when you use ref on a component, it’s a reference to 28 | // the component (not the underlying element), so you have access to its methods. 29 | this.InputComponent.focus(); 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 | 36 | { this.InputComponent = comp; }} 38 | /> 39 |
40 | ) 41 | } 42 | } 43 | ``` 44 | ### Reference: 45 | - https://hackernoon.com/10-react-mini-patterns-c1da92f068c5 46 | 47 | -------------------------------------------------------------------------------- /patterns/15.list-components.md: -------------------------------------------------------------------------------- 1 | # Lists Components 2 | Lists and other things that are almost components 3 | 4 | Instead of making a separate component for lists I can then generate the results like: 5 | ```javascript 6 | const SearchSuggestions = (props) => { 7 | // renderSearchSuggestion() behaves as a pseudo SearchSuggestion component 8 | // keep it self contained and it should be easy to extract later if needed 9 | const renderSearchSuggestion = listItem => ( 10 |
  • {listItem.name} {listItem.id}
  • 11 | ); 12 | 13 | return ( 14 |
      15 | {props.listItems.map(renderSearchSuggestion)} 16 |
    17 | ); 18 | }; 19 | ``` 20 | If things get more complex or you want to use this component elsewhere, 21 | you should be able to copy/paste the code out into a new component. 22 | Don’t prematurely componentize. 23 | 24 | ### Related links: 25 | - https://hackernoon.com/10-react-mini-patterns-c1da92f068c5 26 | 27 | -------------------------------------------------------------------------------- /patterns/16.format-text-via-component.md: -------------------------------------------------------------------------------- 1 | # Components for formatting text 2 | Instead of formatting text by calling helper functions inside render, we can create a separate component that 3 | handles this. 4 | 5 | #### With Component 6 | Render function is lot cleaner to comprehend as it is just simple component composition. 7 | ```javascript 8 | const Price = (props) => { 9 | // toLocaleString is not React specific syntax - it is a native JavaScript function used fo formatting 10 | // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString 11 | const price = props.children.toLocaleString('en', { 12 | style: props.showSymbol ? 'currency' : undefined, 13 | currency: props.showSymbol ? 'USD' : undefined, 14 | maximumFractionDigits: props.showDecimals ? 2 : 0 15 | }); 16 | 17 | return {price} 18 | }; 19 | 20 | Price.propTypes = { 21 | className: PropTypes.string, 22 | children: PropTypes.number, 23 | showDecimals: PropTypes.bool, 24 | showSymbol: PropTypes.bool 25 | }; 26 | 27 | Price.defaultProps = { 28 | children: 0, 29 | showDecimals: true, 30 | showSymbol: true, 31 | }; 32 | 33 | const Page = () => { 34 | const lambPrice = 1234.567; 35 | const jetPrice = 999999.99; 36 | const bootPrice = 34.567; 37 | 38 | return ( 39 |
    40 |

    One lamb is {lambPrice}

    41 |

    One jet is {jetPrice}

    42 |

    Those gumboots will set ya back 43 | 46 | {bootPrice} 47 | 48 | bucks. 49 |

    50 |
    51 | ); 52 | }; 53 | ``` 54 | #### Without Component 55 | Less code: But render looks less clean. (Debatable, yeah I understand) 56 | ```javascript 57 | function numberToPrice(num, options = {}) { 58 | const showSymbol = options.showSymbol !== false; 59 | const showDecimals = options.showDecimals !== false; 60 | 61 | return num.toLocaleString('en', { 62 | style: showSymbol ? 'currency' : undefined, 63 | currency: showSymbol ? 'USD' : undefined, 64 | maximumFractionDigits: showDecimals ? 2 : 0 65 | }); 66 | } 67 | 68 | const Page = () => { 69 | const lambPrice = 1234.567; 70 | const jetPrice = 999999.99; 71 | const bootPrice = 34.567; 72 | 73 | return ( 74 |
    75 |

    One lamb is {numberToPrice(lambPrice)}

    76 |

    One jet is {numberToPrice(jetPrice, { showDecimals: false })}

    77 |

    Those gumboots will set ya back 78 | {numberToPrice(bootPrice, { showDecimals: false, showSymbol: false })} 79 | bucks.

    80 |
    81 | ); 82 | }; 83 | ``` 84 | 85 | ### Reference: 86 | - [10 React Mini Patterns](https://hackernoon.com/10-react-mini-patterns-c1da92f068c5) 87 | 88 | -------------------------------------------------------------------------------- /patterns/17.react-fragments.md: -------------------------------------------------------------------------------- 1 | # React Fragments 2 | 3 | React fragments are used whenever a component needs returned with multiple children. 4 | 5 | Specifically, fragments are useful when I don't want to clutter the DOM with unnecessary `
    ` tags that are used purely to wrap children in a React render method. 6 | 7 | For example, React fragments are commonly used to render list items: 8 | ```javascript 9 | render() { 10 | return ( 11 | 12 | Table Cell 1 13 | Table Cell 2 14 | 15 | ); 16 | } 17 | ``` 18 | 19 | This solves the issue of breaking the DOM HTML table specifications by not adding unnecessary `
    ` elements around `` elements. 20 | Do keep in mind, if it is a list being rendered, React does still throw warnings when children don't have the `key={}` prop. 21 | 22 | ### Related links: 23 | - https://reactjs.org/docs/fragments.html 24 | -------------------------------------------------------------------------------- /patterns/18.share-tracking-logic.md: -------------------------------------------------------------------------------- 1 | # Share Tracking Logic 2 | Using HOC to share tracking logic across various UX components 3 | 4 | eg. Adding analytics tracking across various components. 5 | 6 | - Helps to keep it DRY (Do not Repeat Yourself) 7 | - Removing tracking logic etc. from the presentational component makes it well testable, which is key. 8 | 9 | ```javascript 10 | import tracker from './tracker.js'; 11 | 12 | // HOC 13 | const pageLoadTracking = (ComposedComponent) => class HOC extends Component { 14 | componentDidMount() { 15 | tracker.trackPageLoad(this.props.trackingData); 16 | } 17 | 18 | componentDidUpdate() { 19 | tracker.trackPageLoad(this.props.trackingData); 20 | } 21 | 22 | render() { 23 | return 24 | } 25 | }; 26 | 27 | // Usage 28 | import LoginComponent from "./login"; 29 | 30 | const LoginWithTracking = pageLoadTracking(LoginComponent); 31 | 32 | class SampleComponent extends Component { 33 | render() { 34 | const trackingData = {/** Nested Object **/}; 35 | return 36 | } 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /patterns/2.async-nature-of-setState.md: -------------------------------------------------------------------------------- 1 | # Async Nature Of setState() 2 | On the Async nature of setState() 3 | 4 | ## Gist: 5 | React batches updates and flushes it out once per frame (perf optimization) 6 | However, in some cases React has no control over batching, hence updates are made synchronously 7 | e.g. eventListeners, Ajax, setTimeout and similar Web APIs. 8 | 9 | 10 | ### Main Idea 11 | setState() does not immediately mutate this.state but creates a pending state transition. 12 | Accessing this.state after calling this method can potentially return the existing value. 13 | There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains. 14 | 15 | #### Run the below code and you will make the following observations: 16 | 17 | You can see that in every situation (addEventListener, setTimeout or AJAX call) the state before and the state after are different. 18 | And that render was called immediately after triggering the setState method. But why is that? 19 | Well, it turns out React does not understand and thus cannot control code that doesn't live inside the library. 20 | Timeouts or AJAX calls for example, are developer authored code that executes outside of the context of React. 21 | 22 | So why does React synchronously update the state in these cases? Well, because it's trying to be as defensive as possible. 23 | Not being in control means it's not able to do any performance optimisations so it's better to update the state on spot and 24 | make sure the code that follows has access to the latest information available. 25 | 26 | ```javascript 27 | class TestComponent extends React.Component { 28 | constructor(...args) { 29 | super(...args); 30 | this.state = { 31 | dollars: 10 32 | }; 33 | this._saveButtonRef = (btn => { this._btnRef = btn }); 34 | [ 35 | '_onTimeoutHandler', 36 | '_onMouseLeaveHandler', 37 | '_onClickHandler', 38 | '_onAjaxCallback', 39 | ].forEach(propToBind => { 40 | this[propToBind] = this[propToBind].bind(this); 41 | }); 42 | } 43 | 44 | componentDidMount() { 45 | // Add custom event via `addEventListener` 46 | // 47 | // The list of supported React events does include `mouseleave` 48 | // via `onMouseLeave` prop 49 | // 50 | // However, we are not adding the event the `React way` - this will have 51 | // effects on how state mutates 52 | // 53 | // Check the list here - https://reactjs.org/docs/events.html 54 | this._btnRef.addEventListener('mouseleave', this._onMouseLeaveHandler); 55 | 56 | // Add JS timeout 57 | // 58 | // Again,outside React `world` - this will also have effects on how state 59 | // mutates 60 | setTimeout(this._onTimeoutHandler, 10000); 61 | 62 | // Make AJAX request 63 | fetch('https://api.github.com/users') 64 | .then(this._onAjaxCallback); 65 | } 66 | 67 | render() { 68 | console.log('State in render: ' + JSON.stringify(this.state)); 69 | 70 | return ( 71 | 76 | ); 77 | } 78 | 79 | _onClickHandler() { 80 | console.log('State before (_onClickHandler): ' + JSON.stringify(this.state)); 81 | this.setState({ 82 | dollars: this.state.dollars + 10 83 | }); 84 | console.log('State after (_onClickHandler): ' + JSON.stringify(this.state)); 85 | } 86 | 87 | _onMouseLeaveHandler() { 88 | console.log('State before (mouseleave): ' + JSON.stringify(this.state)); 89 | this.setState({ 90 | dollars: this.state.dollars + 20 91 | }); 92 | console.log('State after (mouseleave): ' + JSON.stringify(this.state)); 93 | } 94 | 95 | _onTimeoutHandler() { 96 | console.log('State before (timeout): ' + JSON.stringify(this.state)); 97 | this.setState({ 98 | dollars: this.state.dollars + 30 99 | }); 100 | console.log('State after (timeout): ' + JSON.stringify(this.state)); 101 | } 102 | 103 | _onAjaxCallback(response) { 104 | if (response.status !== 200) { 105 | console.log('Error in AJAX call: ' + response.statusText); 106 | return; 107 | } 108 | console.log('State before (AJAX call): ' + JSON.stringify(this.state)); 109 | this.setState({ 110 | dollars: this.state.dollars + 40 111 | }); 112 | console.log('State after (AJAX call): ' + JSON.stringify(this.state)); 113 | } 114 | }; 115 | 116 | // Render to DOM 117 | ReactDOM.render( 118 | , 119 | document.getElementById('app') 120 | ); 121 | ``` 122 | 123 | ### Possible solution? 124 | 125 | We're used to calling setState with one parameter only, but actually, the method's signature support two. 126 | The second argument that you can pass in is a callback function that will always be executed after the state has been updated (whether it's inside React's known context or outside of it). 127 | 128 | #### An example might be: 129 | 130 | ```javascript 131 | _onClickHandler: function _onClickHandler() { 132 | console.log('State before (_onClickHandler): ' + JSON.stringify(this.state)); 133 | this.setState({ 134 | dollars: this.state.dollars + 10 135 | }, () => { 136 | console.log('Here state will always be updated to latest version!'); 137 | console.log('State after (_onClickHandler): ' + JSON.stringify(this.state)); 138 | }); 139 | } 140 | ``` 141 | 142 | #### A note on the async nature of setstate 143 | 144 | To be politically correct, setState, as a method, is always synchronous. 145 | It's just a function that calls something behind the scenes - enqueueState or enqueueCallback on updater. 146 | 147 | In fact, here's setState taken directly from React source code: 148 | 149 | ```javascript 150 | ReactComponent.prototype.setState = function(partialState, callback) { 151 | invariant( 152 | typeof partialState === 'object' || 153 | typeof partialState === 'function' || 154 | partialState == null, 155 | 'setState(...): takes an object of state variables to update or a ' + 156 | 'function which returns an object of state variables.' 157 | ); 158 | this.updater.enqueueSetState(this, partialState); 159 | if (callback) { 160 | this.updater.enqueueCallback(this, callback, 'setState'); 161 | } 162 | }; 163 | ``` 164 | 165 | What's actually sync or async are the effects of calling setState in a React application - the reconciliation algorithm, doing the VDOM comparisons and calling render to update the real DOM. 166 | 167 | ## Related links: 168 | - https://medium.com/@wereHamster/beware-react-setstate-is-asynchronous-ce87ef1a9cf3#.jhdhncws3 169 | - https://www.bennadel.com/blog/2893-setstate-state-mutation-operation-may-be-synchronous-in-reactjs.htm 170 | -------------------------------------------------------------------------------- /patterns/3.dependency-injection.md: -------------------------------------------------------------------------------- 1 | # Dependency Injection 2 | 3 | In React the need of [dependency injection](https://www.youtube.com/watch?v=IKD2-MAkXyQ) is easily visible. Let's consider the following application tree: 4 | 5 | ```javascript 6 | // Title.jsx 7 | export default function Title(props) { 8 | return

    { props.title }

    ; 9 | } 10 | ``` 11 | 12 | ```javascript 13 | // Header.jsx 14 | import Title from './Title.jsx'; 15 | export default function Header() { 16 | return ( 17 |
    18 | 19 | </header> 20 | ); 21 | } 22 | ``` 23 | ```javascript 24 | // App.jsx 25 | import Header from './Header.jsx'; 26 | class App extends React.Component { 27 | constructor(props) { 28 | super(props); 29 | this.state = { title: 'React Dependency Injection' }; 30 | } 31 | render() { 32 | return <Header />; 33 | } 34 | } 35 | ``` 36 | The string "React Dependency Injection" should somehow reach the Title component. 37 | The direct way of doing this is to pass it from App to Header and then Header to pass it to Title. 38 | However, this may work for these three components but what happens if there are multiple properties and deeper nesting. 39 | Lots of components will have to mention properties that they are not interested in. 40 | It is clear that most React components receive their dependencies via props but the question is how these dependencies reach that point. 41 | 42 | 43 | One way to achieve dependency injection is by using higher-order component to inject data. 44 | 45 | ```javascript 46 | // inject.jsx 47 | var title = 'React Dependency Injection'; 48 | export default function inject(Component) { 49 | return class Injector extends React.Component { 50 | render() { 51 | return ( 52 | <Component 53 | {...this.state} 54 | {...this.props} 55 | title={ title } 56 | /> 57 | ) 58 | } 59 | }; 60 | } 61 | ``` 62 | ```javascript 63 | // Title.jsx 64 | export default function Title(props) { 65 | return <h1>{ props.title }</h1>; 66 | } 67 | ``` 68 | ```javascript 69 | // Header.jsx 70 | import inject from './inject.jsx'; 71 | import Title from './Title.jsx'; 72 | 73 | var EnhancedTitle = inject(Title); 74 | export default function Header() { 75 | return ( 76 | <header> 77 | <EnhancedTitle /> 78 | </header> 79 | ); 80 | } 81 | ``` 82 | The title is hidden in a middle layer (higher-order component) where we pass it as a prop to the original Title component. 83 | That's all nice but it solves only half of the problem. 84 | Now we don't have to pass the title down the tree but how this data will reach the enhance.jsx helper. 85 | 86 | ## Using React's context 87 | 88 | React has the concept of context. The context is something that every component may have access to. 89 | It's something like an event bus but for data. A single model which we can access from everywhere. 90 | 91 | a place where we'll define the context 92 | ```javascript 93 | var context = { title: 'React in patterns' }; 94 | class App extends React.Component { 95 | getChildContext() { 96 | return context; 97 | } 98 | // ... 99 | } 100 | 101 | App.childContextTypes = { 102 | title: PropTypes.string 103 | }; 104 | ``` 105 | A place where we need data 106 | ```javascript 107 | class Inject extends React.Component { 108 | render() { 109 | var title = this.context.title; 110 | // ... 111 | } 112 | } 113 | Inject.contextTypes = { 114 | title: PropTypes.string 115 | }; 116 | ``` 117 | 118 | ### Related links: 119 | - [What is Dependency Injection?](https://www.youtube.com/watch?v=IKD2-MAkXyQ) 120 | - [The Basics of Dependency Injection](https://www.youtube.com/watch?v=jXhdOTw1q5Q) 121 | - [Dependency injection in JavaScript](http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript) 122 | - [DI In React](https://github.com/krasimir/react-in-patterns/blob/master/book/chapter-10/README.md) 123 | -------------------------------------------------------------------------------- /patterns/4.context-wrapper.md: -------------------------------------------------------------------------------- 1 | # Context Wrapper 2 | It is a good practice that our context is not just a plain object but it has an interface that allows us to store and retrieve data. For example: 3 | 4 | ```javascript 5 | // dependencies.js 6 | export default { 7 | data: {}, 8 | get(key) { 9 | return this.data[key]; 10 | }, 11 | register(key, value) { 12 | this.data[key] = value; 13 | } 14 | } 15 | ``` 16 | Then, if we go back to our example, the very top App component may look like that: 17 | ```javascript 18 | import dependencies from './dependencies'; 19 | dependencies.register('title', 'React in patterns'); 20 | 21 | class App extends React.Component { 22 | getChildContext() { 23 | return dependencies; 24 | } 25 | render() { 26 | return <Header />; 27 | } 28 | } 29 | 30 | App.childContextTypes = { 31 | data: PropTypes.object, 32 | get: PropTypes.func, 33 | register: PropTypes.func 34 | }; 35 | ``` 36 | And our Title component gets it's data through the context: 37 | ```javascript 38 | // Title.jsx 39 | export default class Title extends React.Component { 40 | render() { 41 | return <h1>{ this.context.get('title') }</h1> 42 | } 43 | } 44 | Title.contextTypes = { 45 | data: PropTypes.object, 46 | get: PropTypes.func, 47 | register: PropTypes.func 48 | }; 49 | ``` 50 | Ideally we don't want to specify the contextTypes every time when we need an access to the context. 51 | This detail may be wrapped in a higher-order component. 52 | And even more, we may write an utility function that is more descriptive and helps us declare the exact wiring. 53 | ie. instead of accessing the context directly with this.context.get('title') we ask the higher-order component to 54 | get what we need and to pass it as a prop to our component. 55 | For example: 56 | ```javascript 57 | // Title.jsx 58 | import wire from './wire'; 59 | 60 | function Title(props) { 61 | return <h1>{ props.title }</h1>; 62 | } 63 | 64 | export default wire(Title, ['title'], function resolve(title) { 65 | return { title }; 66 | }); 67 | ``` 68 | The wire function accepts first a React component, then an array with all the needed dependencies 69 | (which are registered already) and then a function which I like to call mapper. 70 | It receives what's stored in the context as a raw data and returns an object which is the actual 71 | React props for our component (Title). In this example we just pass what we get - a title string variable. 72 | However, in a real app this could be a collection of data stores, configuration or something else. 73 | So, it's nice that we pass exactly what we need and don't pollute the components with data that they don't need. 74 | 75 | Here is how the wire function looks like: 76 | ```javascript 77 | export default function wire(Component, dependencies, mapper) { 78 | class Inject extends React.Component { 79 | render() { 80 | var resolved = dependencies.map(this.context.get.bind(this.context)); 81 | var props = mapper(...resolved); 82 | 83 | return React.createElement(Component, props); 84 | } 85 | } 86 | Inject.contextTypes = { 87 | data: PropTypes.object, 88 | get: PropTypes.func, 89 | register: PropTypes.func 90 | }; 91 | return Inject; 92 | }; 93 | ``` 94 | Inject is a higher-order component that gets access to the context and retrieves all the items listed under dependencies array. 95 | The mapper is a function receiving the context data and transforms it to props for our component. 96 | 97 | #### Non-context alternative 98 | Use a singleton to register/fetch all dependencies 99 | ```javascript 100 | var dependencies = {}; 101 | 102 | export function register(key, dependency) { 103 | dependencies[key] = dependency; 104 | } 105 | 106 | export function fetch(key) { 107 | if (key in dependencies) return dependencies[key]; 108 | throw new Error(`"${ key } is not registered as dependency.`); 109 | } 110 | 111 | export function wire(Component, deps, mapper) { 112 | return class Injector extends React.Component { 113 | constructor(props) { 114 | super(props); 115 | this._resolvedDependencies = mapper(...deps.map(fetch)); 116 | } 117 | render() { 118 | return ( 119 | <Component 120 | {...this.state} 121 | {...this.props} 122 | {...this._resolvedDependencies} 123 | /> 124 | ); 125 | } 126 | }; 127 | } 128 | ``` 129 | 130 | We'll store the dependencies in dependencies global variable (it's global for our module, not at an application level). 131 | 132 | We then export two functions register and fetch that write and read entries. 133 | 134 | It looks a little bit like implementing setter and getter against a simple JavaScript object. 135 | 136 | Then we have the wire function that accepts our React component and returns a higher-order component. 137 | 138 | In the constructor of that component we are resolving the dependencies and later while rendering the original component we pass them as props. 139 | 140 | We follow the same pattern where we describe what we need (deps argument) and extract the needed props with a mapper function. 141 | 142 | Having the di.jsx helper we are again able to register our dependencies at the entry point of our application (app.jsx) and inject them wherever (Title.jsx) we need. 143 | 144 | ```javascript 145 | // app.jsx 146 | import Header from './Header.jsx'; 147 | import { register } from './di.jsx'; 148 | 149 | register('my-awesome-title', 'React in patterns'); 150 | 151 | class App extends React.Component { 152 | render() { 153 | return <Header />; 154 | } 155 | } 156 | ``` 157 | ```javascript 158 | // Header.jsx 159 | import Title from './Title.jsx'; 160 | 161 | export default function Header() { 162 | return ( 163 | <header> 164 | <Title /> 165 | </header> 166 | ); 167 | } 168 | ``` 169 | ```javascript 170 | // Title.jsx 171 | import { wire } from './di.jsx'; 172 | 173 | var Title = function(props) { 174 | return <h1>{ props.title }</h1>; 175 | }; 176 | 177 | export default wire(Title, ['my-awesome-title'], title => ({ title })); 178 | ``` 179 | If we look at the `Title.jsx` file we'll see that the actual component and the wiring may live in different files. 180 | That way the component and the mapper function become easily unit testable. 181 | -------------------------------------------------------------------------------- /patterns/5.event-handlers.md: -------------------------------------------------------------------------------- 1 | # Event Handlers 2 | Binding event handlers in the constructor. 3 | 4 | Most of the times we handle DOM events in the component that contains the elements dispatching the events. 5 | Like in the example below, we have a click handler and we want to run a function or method of the same component: 6 | 7 | ```javascript 8 | class Switcher extends React.Component { 9 | render() { 10 | return ( 11 | <button onClick={ this._handleButtonClick }> 12 | click me 13 | </button> 14 | ); 15 | } 16 | _handleButtonClick() { 17 | console.log('Button is clicked'); 18 | } 19 | } 20 | ``` 21 | That's all fine because `_handleButtonClick` is a function and we indeed pass a function to the onClick attribute. 22 | 23 | The problem is that as it is the code doesn't keep the scope. So, if we have to use `this` inside `_handleButtonClick` we'll get an error. 24 | ```javascript 25 | class Switcher extends React.Component { 26 | constructor(props) { 27 | super(props); 28 | this.state = { name: 'React in patterns' }; 29 | } 30 | render() { 31 | return ( 32 | <button onClick={ this._handleButtonClick }> 33 | click me 34 | </button> 35 | ); 36 | } 37 | _handleButtonClick() { 38 | console.log(`Button is clicked inside ${ this.state.name }`); 39 | // leads to 40 | // Uncaught TypeError: Cannot read property 'state' of null 41 | } 42 | } 43 | ``` 44 | 45 | What we normally do is to use bind like so: 46 | ```javascript 47 | <button onClick={ this._handleButtonClick.bind(this) }> 48 | click me 49 | </button> 50 | ``` 51 | However, this means that the bind function is called again and again because we may render the button many times. 52 | A better approach would be to create the bindings in the constructor of the component: 53 | ```javascript 54 | class Switcher extends React.Component { 55 | constructor(props) { 56 | super(props); 57 | this.state = { name: 'React in patterns' }; 58 | this._buttonClick = this._handleButtonClick.bind(this); 59 | } 60 | render() { 61 | return ( 62 | <button onClick={ this._buttonClick }> 63 | click me 64 | </button> 65 | ); 66 | } 67 | _handleButtonClick() { 68 | console.log(`Button is clicked inside ${ this.state.name }`); 69 | } 70 | } 71 | ``` 72 | 73 | The other alternative is to use arrow functions for the onClick prop function assignment. 74 | Arrow functions don't affect the context at invocation time (`this` value from the surrounding scope is used). 75 | 76 | Facebook by the way recommend the same technique while dealing with functions that need the context of the same component. 77 | The binding in the constructor may be also useful if we pass callbacks down the tree. 78 | 79 | A short hand example using arrow functions and avoid having to use the constructor: 80 | 81 | ```javascript 82 | class Switcher extends React.Component { 83 | state = { name: 'React in patterns' }; 84 | 85 | render() { 86 | return ( 87 | <button onClick={ this._handleButtonClick }> 88 | click me 89 | </button> 90 | ); 91 | } 92 | _handleButtonClick = () => { 93 | console.log(`Button is clicked inside ${ this.state.name }`); 94 | } 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /patterns/6.flux-pattern.md: -------------------------------------------------------------------------------- 1 | # Flux pattern for data handling 2 | 3 | **Simple dispatcher** 4 | ```javascript 5 | var Dispatcher = function () { 6 | return { 7 | _stores: [], 8 | register: function (store) { 9 | this._stores.push({store: store}); 10 | }, 11 | dispatch: function (action) { 12 | if (this._stores.length > 0) { 13 | this._stores.forEach(function (entry) { 14 | entry.store.update(action); 15 | }); 16 | } 17 | } 18 | } 19 | }; 20 | ``` 21 | We expect the store to have an update method(), so let's modify register to expect it. 22 | ```javascript 23 | function register(store) { 24 | if (!store || !store.update || typeof store.update !== 'function') { 25 | throw new Error('You should provide a store that has an update method'); 26 | } else { 27 | this._stores.push({store: store}); 28 | } 29 | } 30 | ``` 31 | **Full blown Dispatcher** 32 | ```javascript 33 | var Dispatcher = function () { 34 | return { 35 | _stores: [], 36 | register: function (store) { 37 | if (!store || !store.update) { 38 | throw new Error('You should provide a store that has an `update` method.'); 39 | } else { 40 | var consumers = []; 41 | var change = function () { 42 | consumers.forEach(function (l) { 43 | l(store); 44 | }); 45 | }; 46 | var subscribe = function (consumer, noInit) { 47 | consumers.push(consumer); 48 | !noInit ? consumer(store) : null; 49 | }; 50 | 51 | this._stores.push({store: store, change: change}); 52 | return subscribe; 53 | } 54 | }, 55 | dispatch: function (action) { 56 | if (this._stores.length > 0) { 57 | this._stores.forEach(function (entry) { 58 | entry.store.update(action, entry.change); 59 | }); 60 | } 61 | } 62 | } 63 | }; 64 | 65 | module.exports = { 66 | create: function () { 67 | var dispatcher = Dispatcher(); 68 | 69 | return { 70 | createAction: function (type) { 71 | if (!type) { 72 | throw new Error('Please, provide action\'s type.'); 73 | } else { 74 | return function (payload) { 75 | return dispatcher.dispatch({type: type, payload: payload}); 76 | } 77 | } 78 | }, 79 | createSubscriber: function (store) { 80 | return dispatcher.register(store); 81 | } 82 | } 83 | } 84 | }; 85 | ``` 86 | 87 | ### Related links: 88 | - https://github.com/krasimir/react-in-patterns/tree/master/patterns/flux 89 | -------------------------------------------------------------------------------- /patterns/7.one-way-data-flow.md: -------------------------------------------------------------------------------- 1 | # One way data flow 2 | 3 | One-way direction data flow eliminates multiple states and deals with only one which is usually inside the store. 4 | To achieve that our Store object needs logic that allows us to subscribe for changes: 5 | ```javascript 6 | var Store = { 7 | _handlers: [], 8 | _flag: '', 9 | onChange: function (handler) { 10 | this._handlers.push(handler); 11 | }, 12 | set: function (value) { 13 | this._flag = value; 14 | this._handlers.forEach(handler => handler()) 15 | }, 16 | get: function () { 17 | return this._flag; 18 | } 19 | }; 20 | ``` 21 | Then we will hook our main App component and we'll re-render it every time when the Store changes its value: 22 | ```javascript 23 | class App extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | Store.onChange(this.forceUpdate.bind(this)); 27 | } 28 | 29 | render() { 30 | return ( 31 | <div> 32 | <Switcher 33 | value={ Store.get() } 34 | onChange={ Store.set.bind(Store) }/> 35 | </div> 36 | ); 37 | } 38 | } 39 | ``` 40 | Notice that we are using forceUpdate which is not really recommended. 41 | 42 | Normally a high-order component is used to enable the re-rendering. We used forceUpdate just to keep the example simple. 43 | 44 | Because of this change the Switcher becomes really simple. We don't need the internal state: 45 | ```javascript 46 | class Switcher extends React.Component { 47 | constructor(props) { 48 | super(props); 49 | this._onButtonClick = e => { 50 | this.props.onChange(!this.props.value); 51 | } 52 | } 53 | 54 | render() { 55 | return ( 56 | <button onClick={ this._onButtonClick }> 57 | { this.props.value ? 'lights on' : 'lights off' } 58 | </button> 59 | ); 60 | } 61 | } 62 | ``` 63 | The benefit that comes with this pattern is that our components become dummy representation of the Store's data. 64 | It's really easy to think about the React components as views (renderers). 65 | We write our application in a declarative way and deal with the complexity in only one place. 66 | 67 | ### Related links: 68 | - https://www.startuprocket.com/articles/evolution-toward-one-way-data-flow-a-quick-introduction-to-redux 69 | -------------------------------------------------------------------------------- /patterns/8.presentational-vs-container.md: -------------------------------------------------------------------------------- 1 | # Presentational and Container components 2 | 3 | #### Problem 4 | Data and logic together. 5 | ```javascript 6 | class Clock extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {time: this.props.time}; 10 | this._update = this._updateTime.bind(this); 11 | } 12 | 13 | render() { 14 | var time = this._formatTime(this.state.time); 15 | return ( 16 | <h1>{ time.hours } : { time.minutes } : { time.seconds }</h1> 17 | ); 18 | } 19 | 20 | componentDidMount() { 21 | this._interval = setInterval(this._update, 1000); 22 | } 23 | 24 | componentWillUnmount() { 25 | clearInterval(this._interval); 26 | } 27 | 28 | _formatTime(time) { 29 | var [ hours, minutes, seconds ] = [ 30 | time.getHours(), 31 | time.getMinutes(), 32 | time.getSeconds() 33 | ].map(num => num < 10 ? '0' + num : num); 34 | 35 | return {hours, minutes, seconds}; 36 | } 37 | 38 | _updateTime() { 39 | this.setState({time: new Date(this.state.time.getTime() + 1000)}); 40 | } 41 | } 42 | 43 | ReactDOM.render(<Clock time={ new Date() }/>, ...); 44 | ``` 45 | #### Solution 46 | 47 | Let's split the component into two parts - container and presentation. 48 | 49 | #### Container Component 50 | Containers know about data, it's shape and where it comes from. They know details about how the things work or the so called business logic. 51 | They receive information and format it so it is easy to use by the presentational component. Very often we use higher-order components to create containers. 52 | Their render method contains only the presentational component. 53 | 54 | ```javascript 55 | // Clock/index.js 56 | import Clock from './Clock.jsx'; // <-- that's the presentational component 57 | 58 | export default class ClockContainer extends React.Component { 59 | constructor(props) { 60 | super(props); 61 | this.state = {time: props.time}; 62 | this._update = this._updateTime.bind(this); 63 | } 64 | 65 | render() { 66 | return <Clock { ...this._extract(this.state.time) }/>; 67 | } 68 | 69 | componentDidMount() { 70 | this._interval = setInterval(this._update, 1000); 71 | } 72 | 73 | componentWillUnmount() { 74 | clearInterval(this._interval); 75 | } 76 | 77 | _extract(time) { 78 | return { 79 | hours: time.getHours(), 80 | minutes: time.getMinutes(), 81 | seconds: time.getSeconds() 82 | }; 83 | } 84 | 85 | _updateTime() { 86 | this.setState({time: new Date(this.state.time.getTime() + 1000)}); 87 | } 88 | }; 89 | ``` 90 | #### Presentational component 91 | Presentational components are concerned with how the things look. They have the additional markup needed for making the page pretty. 92 | Such components are not bound to anything and have no dependencies. 93 | Very often implemented as a stateless functional components they don't have internal state. 94 | 95 | ```javascript 96 | // Clock/Clock.jsx 97 | export default function Clock(props) { 98 | var [ hours, minutes, seconds ] = [ 99 | props.hours, 100 | props.minutes, 101 | props.seconds 102 | ].map(num => num < 10 ? '0' + num : num); 103 | 104 | return <h1>{ hours } : { minutes } : { seconds }</h1>; 105 | }; 106 | ``` 107 | The nice things about containers is that they encapsulate logic and may inject data into different renderers. 108 | Very often a file that exports a container is not sending out a class directly but a function. 109 | For example, instead of using 110 | 111 | ```javascript 112 | import Clock from './Clock.jsx'; 113 | export default class ClockContainer extends React.Component { 114 | render() { 115 | return <Clock />; 116 | } 117 | } 118 | ``` 119 | We may export a function that accepts the presentational component: 120 | ```javascript 121 | export default function (Component) { 122 | return class Container extends React.Component { 123 | render() { 124 | return <Component />; 125 | } 126 | } 127 | } 128 | ``` 129 | Using this technique our container is really flexible in rendering its result. 130 | It will be really helpful if we want to switch from digital to analog clock representation. 131 | 132 | ### Related links: 133 | - https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.mbglcakmp 134 | - https://github.com/krasimir/react-in-patterns/tree/master/patterns/presentational-and-container 135 | - https://medium.com/@learnreact/container-components-c0e67432e005 136 | -------------------------------------------------------------------------------- /patterns/9.third-party-integration.md: -------------------------------------------------------------------------------- 1 | # Third Party Integration 2 | Mixing 3rd party integrations/libraries with React 3 | 4 | In this example we'll see how to mix React and jQuery's UI plugin. 5 | We pick tag-it jQuery plugin for the example. It transforms an unordered list to input field for managing tags: 6 | ```html 7 | <ul> 8 | <li>JavaScript</li> 9 | <li>CSS</li> 10 | </ul> 11 | ``` 12 | To make it work we have to include jQuery, jQuery UI and the tag-it plugin code. It works like that: 13 | 14 | ```javascript 15 | $('<dom element selector>').tagit(); 16 | ``` 17 | We select a DOM element and call tagit(). 18 | 19 | The very first thing that we have to do is to force a single-render of the Tags component. 20 | That's because when React adds the elements in the actual DOM we want to pass the control of them to jQuery. 21 | If we skip this both React and jQuery will work on same DOM elements without knowing for each other. To achieve a single-render we have to use the lifecycle method `shouldComponentUpdate` 22 | 23 | Let's say that we want to programmatically add a new tag to the already running tag-it field. 24 | Such action will be triggered by the React component and needs to use the jQuery API. 25 | We have to find a way to communicate data to Tags component but still keep the single-render approach. 26 | To illustrate the whole process we will add an input field to the App class and a button which if clicked will pass a string to Tags component. 27 | 28 | ```javascript 29 | class App extends React.Component { 30 | constructor(props) { 31 | super(props); 32 | 33 | this._addNewTag = this._addNewTag.bind(this); 34 | this.state = { 35 | tags: ['JavaScript', 'CSS'], 36 | newTag: null 37 | }; 38 | } 39 | 40 | _addNewTag() { 41 | this.setState({newTag: this.refs.field.value}); 42 | } 43 | 44 | render() { 45 | return ( 46 | <div> 47 | <p>Add new tag:</p> 48 | <div> 49 | <input type='text' ref='field'/> 50 | <button onClick={ this._addNewTag }>Add</button> 51 | </div> 52 | <Tags tags={ this.state.tags } newTag={ this.state.newTag }/> 53 | </div> 54 | ); 55 | } 56 | } 57 | ``` 58 | We use the internal state as a data storage for the value of the newly added field. 59 | Every time when we click the button we update the state and trigger re-rendering of Tags component. 60 | However, because of shouldComponentUpdate we update nothing. 61 | The only one change is that we get a value of the newTag prop which may be captured via another lifecycle method - componentWillReceiveProps 62 | ```javascript 63 | class Tags extends React.Component { 64 | componentDidMount() { 65 | this.list = $(this.refs.list); 66 | this.list.tagit(); 67 | } 68 | 69 | shouldComponentUpdate() { 70 | return false; 71 | } 72 | 73 | componentWillReceiveProps(newProps) { 74 | this.list.tagit('createTag', newProps.newTag); 75 | } 76 | 77 | render() { 78 | return ( 79 | <ul ref='list'> 80 | { this.props.tags.map((tag, i) => <li key={ i }>{ tag } </li>) } 81 | </ul> 82 | ); 83 | } 84 | } 85 | ``` 86 | ### Related links: 87 | - https://github.com/krasimir/react-in-patterns/tree/master/patterns/third-party 88 | -------------------------------------------------------------------------------- /perf-tips/01.shouldComponentUpdate-check.md: -------------------------------------------------------------------------------- 1 | # shouldComponentUpdate() check 2 | `shouldComponentUpdate` check to avoid expensive re-renders 3 | 4 | React Components re-render every time their props or state change. 5 | So imagine having to render the entire page every time there in an action. That takes a big load on the browser. 6 | That’s where ShouldComponentUpdate comes in, whenever React is rendering the view it checks to see 7 | if shouldComponentUpdate is returning false/true. So whenever you have a component that’s static do yourself a favor and return false. 8 | Or if is not static check to see if the props/state has changed. 9 | 10 | #### Bad 11 | ```javascript 12 | const AutocompleteItem = (props) => { 13 | const selectedClass = props.selected === true ? "selected" : ""; 14 | var path = parseUri(props.url).path; 15 | path = path.length <= 0 ? props.url : "..." + path; 16 | 17 | return ( 18 | <li 19 | onMouseLeave={props.onMouseLeave} 20 | className={selectedClass}> 21 | <i className="ion-ios-eye" 22 | data-image={props.image} 23 | data-url={props.url} 24 | data-title={props.title} 25 | onClick={props.handlePlanetViewClick}/> 26 | <span 27 | onMouseEnter={props.onMouseEnter} 28 | > 29 | <div className="dot bg-mint"/> 30 | {path} 31 | </span> 32 | </li> 33 | ); 34 | }; 35 | ``` 36 | #### Good 37 | ```javascript 38 | export default class AutocompleteItem extends React.Component { 39 | shouldComponentUpdate(nextProps, nextState) { 40 | return nextProps.url !== this.props.url || 41 | nextProps.selected !== this.props.selected; 42 | } 43 | 44 | render() { 45 | const {props} = this; 46 | const selectedClass = props.selected === true ? "selected" : ""; 47 | var path = parseUri(props.url).path; 48 | path = path.length <= 0 ? props.url : "..." + path; 49 | 50 | return ( 51 | <li 52 | onMouseLeave={props.onMouseLeave} 53 | className={selectedClass}> 54 | <i className="ion-ios-eye" 55 | data-image={props.image} 56 | data-url={props.url} 57 | data-title={props.title} 58 | onClick={props.handlePlanetViewClick}/> 59 | <span 60 | onMouseEnter={props.onMouseEnter}> 61 | <div className="dot bg-mint"/> 62 | {path} 63 | </span> 64 | </li> 65 | ); 66 | } 67 | } 68 | ``` 69 | 70 | ### Related links: 71 | - [React Performance optimization](https://medium.com/@nesbtesh/react-performance-optimization-28ec5b61fff3) 72 | - [React rendering misconception](https://robots.thoughtbot.com/react-rendering-misconception) 73 | -------------------------------------------------------------------------------- /perf-tips/02.pure-component.md: -------------------------------------------------------------------------------- 1 | # Using Pure Components 2 | Pure Components do shallow equality checks in `shouldComponentUpdate` by default. This is intended to prevent renders when neither props nor state have changed. 3 | 4 | Recompose offers a Higher Order Component called `pure` for this purpose and React added `React.PureComponent` in v15.3.0. 5 | 6 | #### Bad 7 | ```javascript 8 | export default (props, context) => { 9 | // ... do expensive compute on props ... 10 | return <SomeComponent {...props} /> 11 | } 12 | ``` 13 | 14 | #### Good 15 | ```javascript 16 | import { pure } from 'recompose'; 17 | // This won't be called when the props DONT change 18 | export default pure((props, context) => { 19 | // ... do expensive compute on props ... 20 | return <SomeComponent someProp={props.someProp}/> 21 | }) 22 | ``` 23 | 24 | #### Better 25 | ```javascript 26 | // This is better mainly because it uses no external dependencies. 27 | import { PureComponent } from 'react'; 28 | 29 | export default class Example extends PureComponent { 30 | // This won't re-render when the props DONT change 31 | render() { 32 | // ... do expensive compute on props ... 33 | return <SomeComponent someProp={props.someProp}/> 34 | } 35 | } 36 | }) 37 | ``` 38 | 39 | ### Related links: 40 | - [Recompose](https://github.com/acdlite/recompose#composition) 41 | - [Higher Order Components with Functional Patterns Using Recompose](https://egghead.io/courses/higher-order-components-with-functional-patterns-using-recompose) 42 | - [React: PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) 43 | - [Pure Components](https://www.fullstackreact.com/30-days-of-react/day-11/) 44 | - [Top 5 Recompose HOCs](https://medium.com/@abhiaiyer/top-5-recompose-hocs-1a4c9cc4566) 45 | -------------------------------------------------------------------------------- /perf-tips/03.reselect.md: -------------------------------------------------------------------------------- 1 | # Using reselect 2 | Use Reselect in Redux connect(mapState) -- to avoid frequent re-render. 3 | 4 | #### Bad 5 | ```javascript 6 | let App = ({otherData, resolution}) => ( 7 | <div> 8 | <DataContainer data={otherData}/> 9 | <ResolutionContainer resolution={resolution}/> 10 | </div> 11 | ); 12 | 13 | const doubleRes = (size) => ({ 14 | width: size.width * 2, 15 | height: size.height * 2 16 | }); 17 | 18 | App = connect(state => { 19 | return { 20 | otherData: state.otherData, 21 | resolution: doubleRes(state.resolution) 22 | } 23 | })(App); 24 | ``` 25 | In this above case every time otherData in the state changes both DataContainer and ResolutionContainer 26 | will be rendered even when the resolution in the state does not change. 27 | This is because the doubleRes function will always return a new resolution object with a new identity. 28 | If doubleRes is written with Reselect the issue goes away: 29 | Reselect memoizes the last result of the function and returns it when called until new arguments are passed to it. 30 | 31 | #### Good 32 | ```javascript 33 | import { createSelector } from 'reselect'; 34 | const doubleRes = createSelector( 35 | r => r.width, 36 | r => r.height, 37 | (width, height) => ({ 38 | width: width * 2, 39 | height: height * 2 40 | }) 41 | ); 42 | ``` 43 | ### Related links: 44 | - [React](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.cz2ypc2ob) 45 | - [Computing Derived Data: Docs](http://redux.js.org/docs/recipes/ComputingDerivedData.html) 46 | -------------------------------------------------------------------------------- /perf-tips/README.md: -------------------------------------------------------------------------------- 1 | # Perf Tips 2 | 3 | **Key Ideas** 4 | 5 | - Avoid Reconciliation with shouldComponentUpdate() check 6 | 7 | - Use Immutable Data Structures 8 | 9 | - Use Production Build 10 | 11 | - Profile Components with Chrome Timeline 12 | 13 | - Defer computationally expensive tasks in componentWillMount/componentDidMount by using setTimeout and/or requestAnimationFrame 14 | 15 | ## Articles 16 | 17 | [Optimizing Performance: Docs](https://reactjs.org/docs/optimizing-performance.html) 18 | 19 | [Performance Engineering with React](http://benchling.engineering/performance-engineering-with-react/) 20 | 21 | [Tips to optimise rendering of a set of elements in React](https://blog.lavrton.com/how-to-optimise-rendering-of-a-set-of-elements-in-react-ad01f5b161ae) 22 | 23 | [React.js Best Practices for 2016](https://blog.risingstack.com/react-js-best-practices-for-2016/) 24 | -------------------------------------------------------------------------------- /styling/01.stateless-ui-components.md: -------------------------------------------------------------------------------- 1 | # Styling in stateless UI components 2 | Keep styles separated from the parts of the app that are tied to state. 3 | That means routes, views, containers, forms, layouts, etc. should not have any styling or classes in them. 4 | Instead, these heavy-lifting components should be composed of primarily stateless functional UI components. 5 | 6 | Form component (with no styles/classNames) - just pure composed components 7 | ```javascript 8 | class SampleComponent extends Component { 9 | render() { 10 | return ( 11 | <form onSubmit={this.handleSubmit}> 12 | <Heading children='Sign In'/> 13 | <Input 14 | name='username' 15 | value={username} 16 | onChange={this.handleChange}/> 17 | <Input 18 | type='password' 19 | name='password' 20 | value={password} 21 | onChange={this.handleChange}/> 22 | <Button 23 | type='submit' 24 | children='Sign In'/> 25 | </form> 26 | ) 27 | } 28 | } 29 | 30 | // Presentational component (with Styles) 31 | const Button = ({ 32 | ...props 33 | }) => { 34 | const sx = { 35 | fontFamily: 'inherit', 36 | fontSize: 'inherit', 37 | fontWeight: 'bold', 38 | textDecoration: 'none', 39 | display: 'inline-block', 40 | margin: 0, 41 | paddingTop: 8, 42 | paddingBottom: 8, 43 | paddingLeft: 16, 44 | paddingRight: 16, 45 | border: 0, 46 | color: 'white', 47 | backgroundColor: 'blue', 48 | WebkitAppearance: 'none', 49 | MozAppearance: 'none' 50 | } 51 | 52 | return ( 53 | <button {...props} style={sx}/> 54 | ) 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /styling/02.styles-module.md: -------------------------------------------------------------------------------- 1 | # Styles module 2 | Generally, hard coding styles values in a component should be avoided. 3 | Any values that are likely to be used across different UI components should be split into their own module. 4 | 5 | ```javascript 6 | // Styles module 7 | export const white = '#fff'; 8 | export const black = '#111'; 9 | export const blue = '#07c'; 10 | 11 | export const colors = { 12 | white, 13 | black, 14 | blue 15 | }; 16 | 17 | export const space = [ 18 | 0, 19 | 8, 20 | 16, 21 | 32, 22 | 64 23 | ]; 24 | 25 | const styles = { 26 | bold: 600, 27 | space, 28 | colors 29 | }; 30 | 31 | export default styles 32 | ``` 33 | 34 | #### Usage 35 | ```javascript 36 | // button.jsx 37 | import React from 'react' 38 | import { bold, space, colors } from './styles' 39 | 40 | const Button = ({ 41 | ...props 42 | }) => { 43 | const sx = { 44 | fontFamily: 'inherit', 45 | fontSize: 'inherit', 46 | fontWeight: bold, 47 | textDecoration: 'none', 48 | display: 'inline-block', 49 | margin: 0, 50 | paddingTop: space[1], 51 | paddingBottom: space[1], 52 | paddingLeft: space[2], 53 | paddingRight: space[2], 54 | border: 0, 55 | color: colors.white, 56 | backgroundColor: colors.blue, 57 | WebkitAppearance: 'none', 58 | MozAppearance: 'none' 59 | }; 60 | 61 | return ( 62 | <button {...props} style={sx}/> 63 | ) 64 | }; 65 | ``` 66 | -------------------------------------------------------------------------------- /styling/03.style-functions.md: -------------------------------------------------------------------------------- 1 | # Style Functions 2 | Since we’re using JavaScript, we can also employ helper functions for styling elements. 3 | 4 | #### Example 1 5 | 6 | A function to create rgba values of black 7 | ```javascript 8 | const darken = (n) => `rgba(0, 0, 0, ${n})`; 9 | darken(1 / 8); // 'rgba(0, 0, 0, 0.125)' 10 | 11 | const shade = [ 12 | darken(0), 13 | darken(1 / 8), 14 | darken(1 / 4), 15 | darken(3 / 8), 16 | darken(1 / 2), 17 | darken(5 / 8), 18 | darken(3 / 4), 19 | darken(7 / 8), 20 | darken(1) 21 | ]; 22 | // So now, 23 | // shade[4] is 'rgba(0, 0, 0, 0.5)' 24 | ``` 25 | 26 | #### Example 2 27 | 28 | Creating a scale for margin and padding to help keep visual rhythm consistent 29 | ```javascript 30 | // Modular powers of two scale 31 | const scale = [ 32 | 0, 33 | 8, 34 | 16, 35 | 32, 36 | 64 37 | ]; 38 | 39 | // Functions to get partial style objects 40 | const createScaledPropertyGetter = (scale) => (prop) => (x) => { 41 | return (typeof x === 'number' && typeof scale[x] === 'number') 42 | ? {[prop]: scale[x]} 43 | : null 44 | }; 45 | const getScaledProperty = createScaledPropertyGetter(scale); 46 | 47 | export const getMargin = getScaledProperty('margin'); 48 | export const getPadding = getScaledProperty('padding'); 49 | // Style function usage 50 | const Box = ({ 51 | m, 52 | p, 53 | ...props 54 | }) => { 55 | const sx = { 56 | ...getMargin(m), 57 | ...getPadding(p) 58 | }; 59 | 60 | return <div {...props} style={sx}/> 61 | }; 62 | 63 | // Component usage 64 | const Box = () => ( 65 | <div> 66 | <Box m={2} p={3}> 67 | A box with 16px margin and 32px padding 68 | </Box> 69 | </div> 70 | ); 71 | ``` 72 | -------------------------------------------------------------------------------- /styling/04.using-npm-modules.md: -------------------------------------------------------------------------------- 1 | # Using npm modules 2 | For more complex color/style transformation logic, it's always good to use it from a separate npm module (or) create one. 3 | 4 | #### Example 5 | 6 | For darkening scales in CSS you can use `chroma-js` module 7 | ```javascript 8 | import chroma from 'chroma-js' 9 | 10 | const alpha = (color) => (a) => chroma(color).alpha(a).css(); 11 | 12 | const darken = alpha('#000'); 13 | 14 | const shade = [ 15 | darken(0), 16 | darken(1 / 8), 17 | darken(1 / 4) 18 | // More... 19 | ]; 20 | 21 | const blueAlpha = [ 22 | alpha(blue)(0), 23 | alpha(blue)(1 / 4), 24 | alpha(blue)(1 / 2), 25 | alpha(blue)(3 / 4), 26 | alpha(blue)(1) 27 | ]; 28 | ``` 29 | -------------------------------------------------------------------------------- /styling/05.base-component.md: -------------------------------------------------------------------------------- 1 | # Base Component 2 | ### Using a Base Component 3 | There is tremendous amount of flexibility when it comes to composition in React – since components are essentially just functions. 4 | By changing style details in a component to props we can make it more reusable. 5 | 6 | The color and backgroundColor properties have been moved up to the component’s props. 7 | Additionally, we’ve added a big prop to adjust the padding top and bottom. 8 | ```javascript 9 | const Button = ({ 10 | big, 11 | color = colors.white, 12 | backgroundColor = colors.blue, 13 | ...props 14 | }) => { 15 | const sx = { 16 | fontFamily: 'inherit', 17 | fontSize: 'inherit', 18 | fontWeight: bold, 19 | textDecoration: 'none', 20 | display: 'inline-block', 21 | margin: 0, 22 | paddingTop: big ? space[2] : space[1], 23 | paddingBottom: big ? space[2] : space[1], 24 | paddingLeft: space[2], 25 | paddingRight: space[2], 26 | border: 0, 27 | color, 28 | backgroundColor, 29 | WebkitAppearance: 'none', 30 | MozAppearance: 'none' 31 | }; 32 | 33 | return ( 34 | <button {...props} style={sx}/> 35 | ) 36 | }; 37 | ``` 38 | #### Usage 39 | ```javascript 40 | const Button = () => ( 41 | <div> 42 | <Button> 43 | Blue Button 44 | </Button> 45 | <Button big backgroundColor={colors.red}> 46 | Big Red Button 47 | </Button> 48 | </div> 49 | ); 50 | 51 | // By adjusting the props API of the base Button component, 52 | // an entire set of button styles can be created. 53 | const ButtonBig = (props) => <Button {...props} big/>; 54 | const ButtonGreen = (props) => <Button {...props} backgroundColor={colors.green}/>; 55 | const ButtonRed = (props) => <Button {...props} backgroundColor={colors.red}/>; 56 | const ButtonOutline = (props) => <Button {...props} outline/>; 57 | ``` 58 | -------------------------------------------------------------------------------- /styling/06.layout-component.md: -------------------------------------------------------------------------------- 1 | # Layout component 2 | We can extend the idea of [Base components](./05.base-component.md) to create Layout components. 3 | 4 | #### Example 5 | ```javascript 6 | const Grid = (props) => ( 7 | <Box {...props} 8 | display='inline-block' 9 | verticalAlign='top' 10 | px={2}/> 11 | ); 12 | 13 | const Half = (props) => ( 14 | <Grid {...props} 15 | width={1 / 2}/> 16 | ); 17 | 18 | const Third = (props) => ( 19 | <Grid {...props} 20 | width={1 / 3}/> 21 | ); 22 | 23 | const Quarter = (props) => ( 24 | <Grid {...props} 25 | width={1 / 4}/> 26 | ); 27 | 28 | const Flex = (props) => ( 29 | <Box {...props} 30 | display='flex'/> 31 | ); 32 | 33 | const FlexAuto = (props) => ( 34 | <Box {...props} 35 | flex='1 1 auto'/> 36 | ); 37 | ``` 38 | 39 | #### Usage 40 | ```javascript 41 | const Layout = () => ( 42 | <div> 43 | <div> 44 | <Half>Half width column</Half> 45 | <Half>Half width column</Half> 46 | </div> 47 | <div> 48 | <Third>Third width column</Third> 49 | <Third>Third width column</Third> 50 | <Third>Third width column</Third> 51 | </div> 52 | <div> 53 | <Quarter>Quarter width column</Quarter> 54 | <Quarter>Quarter width column</Quarter> 55 | <Quarter>Quarter width column</Quarter> 56 | <Quarter>Quarter width column</Quarter> 57 | </div> 58 | </div> 59 | ); 60 | ``` 61 | 62 | ### Related links: 63 | - [Github: React Layout components](https://github.com/rofrischmann/react-layout-components) 64 | - [Leveling Up With React: Container Components](https://css-tricks.com/learning-react-container-components/) 65 | - [Container Components and Stateless Functional Components in React](Leveling Up With React: Container Components) -------------------------------------------------------------------------------- /styling/07.typography-component.md: -------------------------------------------------------------------------------- 1 | # Typography Component 2 | We can extend the idea of [Base components](./05.base-component.md) to create Typography components 3 | this pattern helps ensure consistency and keep your styling DRY. 4 | 5 | #### Example 6 | ```javascript 7 | import React from 'react'; 8 | import { alternateFont, typeScale, boldFontWeight } from './styles'; 9 | 10 | const Text = ({ 11 | tag = 'span', 12 | size = 4, 13 | alt, 14 | center, 15 | bold, 16 | caps, 17 | ...props 18 | }) => { 19 | const Tag = tag; 20 | const sx = { 21 | fontFamily: alt ? alternateFont : null, 22 | fontSize: typeScale[size], 23 | fontWeight: bold ? boldFontWeight : null, 24 | textAlign: center ? 'center' : null, 25 | textTransform: caps ? 'uppercase' : null 26 | }; 27 | 28 | return <Tag {...props} style={sx}/> 29 | }; 30 | 31 | const LeadText = (props) => <Text {...props} tag='p' size={3}/>; 32 | const Caps = (props) => <Text {...props} caps/>; 33 | const MetaText = (props) => <Text {...props} size={5} caps/>; 34 | const AltParagraph = (props) => <Text {...props} tag='p' alt/>; 35 | 36 | const CapsButton = ({ children, ...props }) => ( 37 | <Button {...props}> 38 | <Caps> 39 | {children} 40 | </Caps> 41 | </Button> 42 | ); 43 | ``` 44 | #### Usage 45 | ```javascript 46 | const TypographyComponent = () => ( 47 | <div> 48 | <LeadText> 49 | This is a lead with some<Caps>all caps</Caps>. 50 | It has a larger font size than the default paragraph. 51 | </LeadText> 52 | <MetaText> 53 | This is smaller text, like form helper copy. 54 | </MetaText> 55 | </div> 56 | ); 57 | ``` 58 | -------------------------------------------------------------------------------- /styling/08.HOC-for-styling.md: -------------------------------------------------------------------------------- 1 | # Using HOC for styling 2 | Sometimes there are isolated UI components that only require a minimal amount of state for interaction, and using them as standalone components is sufficient 3 | 4 | eg. Interactions in a Carousel 5 | 6 | #### Example: Carousel 7 | 8 | The HOC will have a current slide index and have previous and next methods. 9 | ```javascript 10 | // Higher order component 11 | import React from 'react' 12 | // This could be named something more generic like Counter or Cycle 13 | const CarouselContainer = (Comp) => { 14 | class Carousel extends React.Component { 15 | constructor() { 16 | super(); 17 | this.state = { 18 | index: 0 19 | }; 20 | this.previous = () => { 21 | const { index } = this.state; 22 | if (index > 0) { 23 | this.setState({index: index - 1}) 24 | } 25 | }; 26 | 27 | this.next = () => { 28 | const { index } = this.state; 29 | this.setState({index: index + 1}) 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 | <Comp 36 | {...this.props} 37 | {...this.state} 38 | previous={this.previous} 39 | next={this.next}/> 40 | ) 41 | } 42 | } 43 | return Carousel 44 | }; 45 | export default CarouselContainer; 46 | ``` 47 | #### Using the HOC 48 | ```javascript 49 | // UI component 50 | const Carousel = ({ index, ...props }) => { 51 | const length = props.length || props.children.length || 0; 52 | 53 | const sx = { 54 | root: { 55 | overflow: 'hidden' 56 | }, 57 | inner: { 58 | whiteSpace: 'nowrap', 59 | height: '100%', 60 | transition: 'transform .2s ease-out', 61 | transform: `translateX(${index % length * -100}%)` 62 | }, 63 | child: { 64 | display: 'inline-block', 65 | verticalAlign: 'middle', 66 | whiteSpace: 'normal', 67 | outline: '1px solid red', 68 | width: '100%', 69 | height: '100%' 70 | } 71 | }; 72 | 73 | const children = React.Children.map(props.children, (child, i) => { 74 | return ( 75 | <div style={sx.child}> 76 | {child} 77 | </div> 78 | ) 79 | }); 80 | 81 | return ( 82 | <div style={sx.root}> 83 | <div style={sx.inner}> 84 | {children} 85 | </div> 86 | </div> 87 | ) 88 | }; 89 | 90 | // Final Carousel component 91 | const HeroCarousel = (props) => { 92 | return ( 93 | <div> 94 | <Carousel index={props.index}> 95 | <div>Slide one</div> 96 | <div>Slide two</div> 97 | <div>Slide three</div> 98 | </Carousel> 99 | <Button 100 | onClick={props.previous} 101 | children='Previous'/> 102 | <Button 103 | onClick={props.next} 104 | children='Next'/> 105 | </div> 106 | ) 107 | }; 108 | 109 | // Wrap the component with the functionality from the higher order component 110 | export default CarouselContainer(HeroCarousel) 111 | ``` 112 | By keeping the styling separate from the interactive state, any number of carousel variations can be created from these reusable parts. 113 | 114 | #### Usage 115 | ```javascript 116 | const Carousel = () => ( 117 | <div> 118 | <HeroCarousel /> 119 | </div> 120 | ); 121 | ``` 122 | -------------------------------------------------------------------------------- /styling/README.md: -------------------------------------------------------------------------------- 1 | # Styling in React 2 | 3 | Here we look into some ideas around using CSS in JS. 4 | 5 | If you are pondering over why to use CSS in JS, I highly recommend this [talk](http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html) by [Vjeux](https://twitter.com/Vjeux) 6 | 7 | ## Articles 8 | 9 | [Patterns for style composition in React](https://jxnblk.com/blog/patterns-for-style-composition-in-react/) 10 | 11 | [Inline style vs stylesheet performance](https://www.ctheu.com/2015/08/17/react-inline-styles-vs-css-stupid-benchmark/) 12 | -------------------------------------------------------------------------------- /ux-variations/01.composing-variations.md: -------------------------------------------------------------------------------- 1 | # Using Composition to handle UX variations 2 | Combining smaller reusable components to build a bigger UI blocks. 3 | 4 | **How do we make sure components are reusable?** 5 | - By ensuring our UI components are pure presentational components (dumb) 6 | 7 | **What does reusable mean?** 8 | - No data fetching within the component (do it in Redux). 9 | - If data is required from API - goes into Redux 10 | Via redux-thunk API calls are isolated away from the redux containers that deal with the data obtained and pass it on to the dumb component. 11 | 12 | If we have a bunch of renderBla() functions within the component which are used in the main component render() 13 | - It’s better to move it to separate components. That way it is reusable. 14 | 15 | ### Example 16 | Login page variations 17 | 18 | UX variations toggle features + add in additional links/markup. 19 | 20 | 21 | If the UX variations are involved toggling features within a component + adding minor markup around it 22 | 23 | ```javascript 24 | import React, { Component } from "react"; 25 | import PropTypes from 'prop-types'; 26 | import SignIn from "./sign-in"; 27 | 28 | class MemberSignIn extends Component { 29 | _renderMemberJoinLinks() { 30 | return ( 31 | <div className="member-signup-links"> 32 | ... 33 | </div> 34 | ); 35 | } 36 | 37 | _routeTo() { 38 | // Routing logic here 39 | } 40 | 41 | render() { 42 | const {forgotEmailRoute,forgotPwdRoute, showMemberSignupLinks} = this.props; 43 | return ( 44 | <div> 45 | <SignIn 46 | onForgotPasswordRequested={this._routeTo(forgotPwdRoute)} 47 | onForgotEmailRequested={this._routeTo(forgotEmailRoute)}> 48 | {this.props.children} 49 | {showMemberSignupLinks && this._renderMemberJoinLinks()} 50 | </SignIn> 51 | </div> 52 | ); 53 | } 54 | } 55 | 56 | export default MemberSignIn; 57 | ``` 58 | 59 | ### Related links: 60 | - [Slides from my talk: Building Multi-tenant UI with React](https://speakerdeck.com/vasa/building-multitenant-ui-with-react-dot-js) -------------------------------------------------------------------------------- /ux-variations/02.toggle-ui-elements.md: -------------------------------------------------------------------------------- 1 | # Toggle UI Elements 2 | Handling minor UX variations in the component by toggling ON/OFF features. 3 | 4 | Modify the component to take in a prop to control it’s behavior. 5 | 6 | ### Gotcha: 7 | Easy to overuse this idea by adding props for every variation. 8 | - Only add in props for features specific to the current feature that the component. 9 | - Basically, not violate the Single Responsibility Principle. 10 | 11 | #### Example 12 | Show/Hide password feature in Login form 13 | 14 | ```javascript 15 | class PasswordField extends Component { 16 | render() { 17 | const { 18 | password, 19 | showHidePassword, 20 | showErrorOnTop, 21 | showLabels, 22 | shouldComplyAda 23 | } = this.props; 24 | return ( 25 | <div> 26 | <Password 27 | field={password} 28 | label="Password" 29 | showErrorOnTop={showErrorOnTop} 30 | placeholder={shouldComplyAda ? "" : "Password"} 31 | showLabel={showLabels} 32 | showHidePassword={showHidePassword} 33 | /> 34 | </div> 35 | ); 36 | } 37 | } 38 | ``` 39 | 40 | ### Related links: 41 | - [Slides from my talk: Building Multi-tenant UI with React](https://speakerdeck.com/vasa/building-multitenant-ui-with-react-dot-js) 42 | -------------------------------------------------------------------------------- /ux-variations/03.HOC-feature-toggles.md: -------------------------------------------------------------------------------- 1 | # HOC for Feature Toggles 2 | Using Higher order components (HOC) for UX variations 3 | 4 | eg. Toggling features On/Off 5 | 6 | ```javascript 7 | // featureToggle.js 8 | const isFeatureOn = function (featureName) { 9 | // return true or false 10 | }; 11 | 12 | import { isFeatureOn } from './featureToggle'; 13 | 14 | const toggleOn = (featureName, ComposedComponent) => class HOC extends Component { 15 | render() { 16 | return isFeatureOn(featureName) ? <ComposedComponent {...this.props} /> : null; 17 | } 18 | }; 19 | 20 | // Usage 21 | import AdsComponent from './Ads' 22 | const Ads = toggleOn('ads', AdsComponent); 23 | ``` 24 | -------------------------------------------------------------------------------- /ux-variations/04.HOC-props-proxy.md: -------------------------------------------------------------------------------- 1 | # Higher Order Component - Props proxy 2 | This basically helps to add/edit props passed to the Component. 3 | 4 | ```javascript 5 | function HOC(WrappedComponent) { 6 | return class Test extends Component { 7 | render() { 8 | const newProps = { 9 | title: 'New Header', 10 | footer: false, 11 | showFeatureX: false, 12 | showFeatureY: true 13 | }; 14 | 15 | return <WrappedComponent {...this.props} {...newProps} /> 16 | } 17 | } 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /ux-variations/05.wrapper-components.md: -------------------------------------------------------------------------------- 1 | # Wrapper Components 2 | Using Wrappers to handle UX/style variations 3 | 4 | For Handling Wrapper ``<div>``’s and other markup around component, use composition! 5 | 6 | When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags. 7 | Parent can read its children by accessing the special this.props.children prop. 8 | 9 | ```javascript 10 | const SampleComponent = () => { 11 | <Parent> 12 | <Child /> 13 | </Parent> 14 | }; 15 | 16 | const Parent = () => { 17 | // You can use class 'bla' or any other classes to handle any style variations for the same markup. 18 | <div className="bla"> 19 | {this.props.children} 20 | </div> 21 | }; 22 | ``` 23 | 24 | FYI - Wrapper component can also be made accept a tag name and then used to generate the HTML element. 25 | However, usually this is not recommended because you can't add attributes/props to it. 26 | 27 | ```javascript 28 | const SampleComponent = () => { 29 | <Wrap tagName="div" content="Hello World" /> 30 | }; 31 | 32 | const Wrap = ({ tagName, content }) => { 33 | const Tag = `${tagName}` // variable name must begin with capital letters 34 | return <Tag>{content}</Tag> 35 | } 36 | ``` 37 | 38 | ### Related links: 39 | - [Slides from my talk: Building Multi-tenant UI with React](https://speakerdeck.com/vasa/building-multitenant-ui-with-react-dot-js) 40 | -------------------------------------------------------------------------------- /ux-variations/06.display-order-variations.md: -------------------------------------------------------------------------------- 1 | # Display UI elements in different order 2 | 3 | Use a prop to specify order – Map through ReactElements and render it based on order prop. 4 | ```javascript 5 | class PageSections extends Component { 6 | render() { 7 | const pageItems = this.props.contentOrder.map( 8 | (content) => { 9 | const renderFunc = this.contentOrderMap[content]; 10 | return (typeof renderFunc === 'function') ? renderFunc() : null; 11 | } 12 | ); 13 | 14 | return ( 15 | <div className="page-content"> 16 | {pageItems} 17 | </div> 18 | ) 19 | } 20 | } 21 | ``` 22 | ### Related links: 23 | - [Slides from my talk: Building Multi-tenant UI with React](https://speakerdeck.com/vasa/building-multitenant-ui-with-react-dot-js) -------------------------------------------------------------------------------- /ux-variations/README.md: -------------------------------------------------------------------------------- 1 | # Handling UX variations for multiple brands and apps 2 | 3 | 4 | ## [Slides from my talk](https://speakerdeck.com/vasa/building-multitenant-ui-with-react-dot-js) 5 | 6 | 7 | A few general coding principles which help write reusable React components 8 | 9 | ## Single Responsibility Principle 10 | 11 | **In React** 12 | 13 | Components/Containers code must essentially deal with only one chunk of the UI feature/functionality. 14 | 15 | * Eg. Shipping Address component 16 | 17 | * Address container (Only has address related fields), Name container (first and last name), Phone component, State, City and Zip code container 18 | 19 | **In Redux** 20 | 21 | All API related call go into Redux thunks/other async handling sections (redux-promise, sagas etc) 22 | 23 | * The thunks are responsible only for the dispatching action on AJAX begin/fail and complete. 24 | 25 | * Any routing has to be dealt with the receiving component via a promise. 26 | 27 | ## Keep it Simple Stupid (KISS) 28 | 29 | * Essentially, if the component needs no state - use stateless functions. 30 | 31 | * Perf matters: **Stateless fns > ES6 class components > React.createClass()** 32 | 33 | * Don’t pass any more props than required {...this.props} only if the list is big -- if not pass individual props. 34 | 35 | * Too much flows of control (If-else variations) inside the component is usually a red-flag. This most likely means - need to split up the component or create a separate variation. 36 | 37 | * Don’t optimize prematurely - Making the current component reusable with current variations known. 38 | 39 | ## Articles 40 | 41 | [Building React Components for Multiple Brands and Applications](https://medium.com/walmartlabs/building-react-components-for-multiple-brands-and-applications-7e9157a39db4) 42 | 43 | --------------------------------------------------------------------------------