├── README.md ├── chapter-03 ├── .gitignore └── reusable-components │ ├── .eslintrc │ ├── .gitignore │ ├── .storybook │ └── config.js │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ ├── src │ ├── App.css │ ├── App.js │ ├── components │ │ ├── item.js │ │ ├── list.js │ │ ├── post-list.js │ │ └── user-list.js │ ├── index.css │ ├── index.js │ └── logo.svg │ └── stories │ └── list.js ├── chapter-04 ├── container-presentational │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── geolocation │ │ │ ├── geolocation-container.js │ │ │ ├── geolocation.js │ │ │ └── index.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── higher-order-components │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ ├── my-component.js │ └── with-inner-width.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── chapter-05 ├── data-fetching │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── list.js │ │ └── with-data.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── data-flow │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── buttons.js │ │ ├── counter.js │ │ └── display.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── react-refetch │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ ├── gist.js │ └── list.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── chapter-06 ├── controlled-components │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── controlled.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── css-transition-group │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── transition.css │ │ └── transition.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── event-switch │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── button.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── json-schema │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── json-schema-form.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── react-motion │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── transition.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── refs-dom │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── focus.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── refs-instance │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── input.js │ │ └── reset.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── svg │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── circle.js │ │ └── red-circle.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── uncontrolled-components │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ └── uncontrolled.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── chapter-07 ├── css-modules │ ├── .eslintrc │ ├── .gitignore │ ├── index.css │ ├── index.js │ ├── package.json │ └── webpack.config.js ├── inline-styles │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── font-size.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── radium │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── button.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── styled-components │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ └── button.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── chapter-08 ├── data-fetching │ ├── .eslintrc │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── app.js │ │ ├── client.js │ │ ├── server.js │ │ └── template.js │ └── webpack.config.js ├── next │ ├── .eslintrc │ ├── .gitignore │ ├── package.json │ └── pages │ │ └── index.js └── server-side-rendering │ ├── .eslintrc │ ├── .gitignore │ ├── package.json │ ├── src │ ├── app.js │ ├── client.js │ ├── server.js │ └── template.js │ └── webpack.config.js ├── chapter-09 ├── constants-props │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── item.js │ │ └── list.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── creating-functions │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── item.js │ │ └── list.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── good-design │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── form.js │ │ ├── list.js │ │ └── todos.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── keys │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ └── list.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── chapter-10 ├── enzyme │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── button.js │ ├── button.spec.js │ └── package.json ├── higher-order-components │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── __snapshots__ │ │ └── list.spec.js.snap │ ├── get-json.js │ ├── list.js │ ├── list.spec.js │ ├── package.json │ ├── with-data.js │ └── with-data.spec.js ├── jest │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── button.js │ ├── button.spec.js │ └── package.json ├── mocha │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── button.js │ ├── package.json │ └── test │ │ └── button.spec.js ├── page-object │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── form.js │ ├── form.spec.js │ ├── package.json │ └── page.js └── real-world │ ├── .babelrc │ ├── .gitignore │ ├── TodoTextInput-snapshot.spec.js │ ├── TodoTextInput.js │ ├── TodoTextInput.spec.js │ ├── __snapshots__ │ └── TodoTextInput-snapshot.spec.js.snap │ └── package.json ├── chapter-11 ├── index-as-key │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── list.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg ├── initializing-state │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── counter.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── mutating-state │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── favicon.ico │ ├── index.html │ ├── package.json │ └── src │ ├── App.css │ ├── App.js │ ├── components │ ├── counter.js │ └── list.js │ ├── index.css │ ├── index.js │ └── logo.svg └── cover.jpg /README.md: -------------------------------------------------------------------------------- 1 | # React Design Patterns and Best Practices 2 | Build modular applications that are easy to scale using the most powerful components and design 3 | patterns that React can offer you right now 4 | 5 | [https://www.packtpub.com/web-development/react-design-patterns-and-best-practices](https://www.packtpub.com/web-development/react-design-patterns-and-best-practices) 6 | 7 | ![React Design Patterns and Best Practices](cover.jpg) 8 | -------------------------------------------------------------------------------- /chapter-03/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-03/.gitignore -------------------------------------------------------------------------------- /chapter-03/reusable-components/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@kadira/storybook' 2 | 3 | function loadStories() { 4 | require('../stories/list') 5 | } 6 | 7 | configure(loadStories, module) 8 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-03/reusable-components/favicon.ico -------------------------------------------------------------------------------- /chapter-03/reusable-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@kadira/storybook": "^2.3.0", 7 | "eslint": "^2.9.0", 8 | "eslint-config-airbnb": "^9.0.1", 9 | "eslint-plugin-import": "^1.7.0", 10 | "eslint-plugin-jsx-a11y": "^1.2.0", 11 | "eslint-plugin-react": "^5.0.1", 12 | "react-scripts": "0.2.2" 13 | }, 14 | "dependencies": { 15 | "react": "^15.3.1", 16 | "react-dom": "^15.3.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src", 23 | "storybook": "start-storybook -p 9001" 24 | }, 25 | "eslintConfig": { 26 | "extends": "./node_modules/react-scripts/config/eslint.js" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import PostList from './components/post-list' 6 | import UserList from './components/user-list' 7 | 8 | class App extends Component { 9 | render() { 10 | return ( 11 |
12 |
13 | logo 14 |

Welcome to React

15 |
16 | 17 | 18 |
19 | ) 20 | } 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/components/item.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Item = ({ text, title }) => ( 4 |
  • 5 |

    {title}

    6 | {text &&

    {text}

    } 7 |
  • 8 | ) 9 | 10 | Item.propTypes = { 11 | text: React.PropTypes.string, 12 | title: React.PropTypes.string, 13 | } 14 | 15 | export default Item 16 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Item from './item' 3 | 4 | const List = ({ collection, textKey, titleKey }) => ( 5 | 10 | ) 11 | 12 | List.propTypes = { 13 | collection: React.PropTypes.array, 14 | textKey: React.PropTypes.string, 15 | titleKey: React.PropTypes.string, 16 | } 17 | 18 | export default List 19 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/components/post-list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from './list' 3 | 4 | const Posts = { 5 | fetch() { 6 | return new Promise(resolve => resolve([{ id: 1, title: 'title', excerpt: 'excerpt' }])) 7 | }, 8 | } 9 | 10 | class PostList extends React.Component { 11 | 12 | constructor(props) { 13 | super(props) 14 | 15 | this.state = { 16 | posts: [], 17 | } 18 | } 19 | 20 | componentDidMount() { 21 | Posts.fetch().then(posts => { 22 | this.setState({ posts }) 23 | }) 24 | } 25 | 26 | render() { 27 | return ( 28 | 29 | ) 30 | } 31 | 32 | } 33 | 34 | export default PostList 35 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/components/user-list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from './list' 3 | 4 | const UserList = ({ users }) => ( 5 | 6 | ) 7 | 8 | UserList.propTypes = { 9 | users: React.PropTypes.array, 10 | } 11 | 12 | export default UserList 13 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-03/reusable-components/stories/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@kadira/storybook' 3 | import List from '../src/components/list' 4 | 5 | const users = [{ id: 1, username: 'username', bio: 'bio' }] 6 | 7 | storiesOf('List', module) 8 | .add('with bio', () => ( 9 | 10 | )) 11 | .add('without bio', () => ( 12 | 13 | )) 14 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-04/container-presentational/favicon.ico -------------------------------------------------------------------------------- /chapter-04/container-presentational/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Geolocation from './components/geolocation' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/components/geolocation/geolocation-container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Geolocation from './geolocation' 3 | 4 | class GeolocationContainer extends React.Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | latitude: null, 11 | longitude: null, 12 | } 13 | 14 | this.handleSuccess = this.handleSuccess.bind(this) 15 | } 16 | 17 | componentDidMount() { 18 | if (navigator.geolocation) { 19 | navigator.geolocation.getCurrentPosition(this.handleSuccess) 20 | } 21 | } 22 | 23 | handleSuccess({ coords }) { 24 | this.setState({ 25 | latitude: coords.latitude, 26 | longitude: coords.longitude, 27 | }) 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 | ) 34 | } 35 | 36 | } 37 | 38 | export default GeolocationContainer 39 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/components/geolocation/geolocation.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Geolocation = ({ latitude, longitude }) => ( 4 |
    5 |
    Latitude: {latitude}
    6 |
    Longitude: {longitude}
    7 |
    8 | ) 9 | 10 | Geolocation.propTypes = { 11 | latitude: React.PropTypes.number, 12 | longitude: React.PropTypes.number, 13 | } 14 | 15 | export default Geolocation 16 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/components/geolocation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './geolocation-container' 2 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-04/container-presentational/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-04/higher-order-components/favicon.ico -------------------------------------------------------------------------------- /chapter-04/higher-order-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import MyComponent from './components/my-component' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/components/my-component.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import withInnerWidth from './with-inner-width' 4 | 5 | const MyComponent = ({ innerWidth }) =>
    innerWidth: {innerWidth}
    6 | 7 | MyComponent.propTypes = { 8 | innerWidth: React.PropTypes.number, 9 | } 10 | 11 | export default withInnerWidth(MyComponent) 12 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/components/with-inner-width.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const withInnerWidth = Component => ( 4 | class extends React.Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | innerWidth: window.innerWidth, 11 | } 12 | 13 | this.handleResize = this.handleResize.bind(this) 14 | } 15 | 16 | componentDidMount() { 17 | window.addEventListener('resize', this.handleResize) 18 | } 19 | 20 | componentWillUnmount() { 21 | window.removeEventListener('resize', this.handleResize) 22 | } 23 | 24 | handleResize() { 25 | this.setState({ 26 | innerWidth: window.innerWidth, 27 | }) 28 | } 29 | 30 | render() { 31 | return 32 | } 33 | 34 | } 35 | ) 36 | 37 | export default withInnerWidth 38 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-04/higher-order-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-05/data-fetching/favicon.ico -------------------------------------------------------------------------------- /chapter-05/data-fetching/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import List from './components/list' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import withData from './with-data' 3 | 4 | const List = ({ data: gists }) => ( 5 |
      6 | {gists.map(gist => ( 7 |
    • {gist.description}
    • 8 | ))} 9 |
    10 | ) 11 | 12 | List.propTypes = { 13 | data: React.PropTypes.array, 14 | } 15 | 16 | const withGists = withData(props => `https://api.github.com/users/${props.username}/gists`) 17 | 18 | export default withGists(List) 19 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/components/with-data.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const withData = url => Component => ( 4 | class extends React.Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { data: [] } 10 | } 11 | 12 | componentDidMount() { 13 | const endpoint = typeof url === 'function' ? url(this.props) : url 14 | 15 | fetch(endpoint) 16 | .then(response => response.json()) 17 | .then(data => this.setState({ data })) 18 | } 19 | 20 | render() { 21 | return 22 | } 23 | } 24 | ) 25 | 26 | export default withData 27 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-05/data-fetching/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-05/data-flow/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-05/data-flow/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-05/data-flow/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-05/data-flow/favicon.ico -------------------------------------------------------------------------------- /chapter-05/data-flow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-05/data-flow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Counter from './components/counter' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/components/buttons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Buttons = ({ onDecrement, onIncrement }) => ( 4 |
    5 | 6 | 7 |
    8 | ) 9 | 10 | Buttons.propTypes = { 11 | onDecrement: React.PropTypes.func, 12 | onIncrement: React.PropTypes.func, 13 | } 14 | 15 | export default Buttons 16 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/components/counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Display from './display' 3 | import Buttons from './buttons' 4 | 5 | class Counter extends React.Component { 6 | 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | counter: 0, 12 | } 13 | 14 | this.handleIncrement = this.handleIncrement.bind(this) 15 | this.handleDecrement = this.handleDecrement.bind(this) 16 | } 17 | 18 | handleIncrement() { 19 | this.setState({ 20 | counter: this.state.counter + 1, 21 | }) 22 | } 23 | 24 | handleDecrement() { 25 | this.setState({ 26 | counter: this.state.counter - 1, 27 | }) 28 | } 29 | 30 | render() { 31 | return ( 32 |
    33 | 34 | 38 |
    39 | ) 40 | } 41 | 42 | } 43 | 44 | export default Counter 45 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/components/display.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Display = ({ counter }) =>

    {counter}

    4 | 5 | Display.propTypes = { 6 | counter: React.PropTypes.number, 7 | } 8 | 9 | export default Display 10 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-05/data-flow/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-05/react-refetch/favicon.ico -------------------------------------------------------------------------------- /chapter-05/react-refetch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1", 16 | "react-refetch": "^1.0.0-beta.8" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import List from './components/list' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/components/gist.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-refetch' 3 | 4 | const Gist = ({ description, star }) => ( 5 |
  • 6 | {description} 7 | 8 |
  • 9 | ) 10 | 11 | Gist.propTypes = { 12 | description: React.PropTypes.string, 13 | star: React.PropTypes.func, 14 | } 15 | 16 | const token = 'access_token=123' 17 | 18 | const connectWithStar = connect(({ id }) => ({ 19 | star: () => ({ 20 | starResponse: { 21 | url: `https://api.github.com/gists/${id}/star?${token}`, 22 | method: 'PUT', 23 | }, 24 | }), 25 | })) 26 | 27 | export default connectWithStar(Gist) 28 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-refetch' 3 | import Gist from './gist' 4 | 5 | const List = ({ gists }) => ( 6 | gists.fulfilled && ( 7 |
      8 | {gists.value.map(gist => ( 9 | 10 | ))} 11 |
    12 | ) 13 | ) 14 | 15 | List.propTypes = { 16 | gists: React.PropTypes.object, 17 | } 18 | 19 | const connectWithGists = connect(({ username }) => ({ 20 | gists: `https://api.github.com/users/${username}/gists`, 21 | })) 22 | 23 | export default connectWithGists(List) 24 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-05/react-refetch/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/controlled-components/favicon.ico -------------------------------------------------------------------------------- /chapter-06/controlled-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Controlled from './components/controlled' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/src/components/controlled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Controlled extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | firstName: 'Dan', 10 | lastName: 'Abramov', 11 | } 12 | 13 | this.handleChange = this.handleChange.bind(this) 14 | this.handleSubmit = this.handleSubmit.bind(this) 15 | } 16 | 17 | handleChange({ target }) { 18 | this.setState({ 19 | [target.name]: target.value, 20 | }) 21 | } 22 | 23 | handleSubmit(e) { 24 | e.preventDefault() 25 | 26 | console.log(`${this.state.firstName} ${this.state.lastName}`) 27 | } 28 | 29 | render() { 30 | return ( 31 |
    32 | 38 | 44 | 45 |
    46 | ) 47 | } 48 | 49 | } 50 | 51 | export default Controlled 52 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/controlled-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/css-transition-group/favicon.ico -------------------------------------------------------------------------------- /chapter-06/css-transition-group/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-addons-css-transition-group": "^15.3.2", 16 | "react-dom": "^15.3.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Transition from './components/transition' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/components/transition.css: -------------------------------------------------------------------------------- 1 | .fade-appear { 2 | opacity: 0.01; 3 | } 4 | 5 | .fade-appear.fade-appear-active { 6 | opacity: 1; 7 | transition: opacity .5s ease-in; 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/components/transition.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group' 3 | import './transition.css' 4 | 5 | const Transition = () => ( 6 | 11 |

    Hello React

    12 |
    13 | ) 14 | 15 | export default Transition 16 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/css-transition-group/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/event-switch/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/event-switch/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/event-switch/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/event-switch/favicon.ico -------------------------------------------------------------------------------- /chapter-06/event-switch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/event-switch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Button from './components/button' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/components/button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Button extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.handleEvent = this.handleEvent.bind(this) 9 | } 10 | 11 | handleEvent(event) { 12 | switch (event.type) { 13 | case 'click': 14 | console.log('clicked') 15 | break 16 | 17 | case 'dblclick': 18 | console.log('double clicked') 19 | break 20 | 21 | default: 22 | console.log('unhandled', event.type) 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 | 29 | ) 30 | } 31 | 32 | } 33 | 34 | export default Button 35 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/event-switch/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/json-schema/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/json-schema/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/json-schema/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/json-schema/favicon.ico -------------------------------------------------------------------------------- /chapter-06/json-schema/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/json-schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1", 16 | "react-jsonschema-form": "^0.40.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import JSONSchemaForm from './components/json-schema-form' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/components/json-schema-form.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Form from 'react-jsonschema-form' 3 | 4 | const schema = { 5 | type: 'object', 6 | properties: { 7 | firstName: { type: 'string', default: 'Dan' }, 8 | lastName: { type: 'string', default: 'Abramov' }, 9 | }, 10 | } 11 | 12 | class JSONSchemaForm extends React.Component { 13 | 14 | constructor(props) { 15 | super(props) 16 | 17 | this.handleSubmit = this.handleSubmit.bind(this) 18 | } 19 | 20 | handleSubmit({ formData }) { 21 | console.log(formData) 22 | } 23 | 24 | render() { 25 | return ( 26 |
    27 | ) 28 | } 29 | 30 | } 31 | 32 | export default JSONSchemaForm 33 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/json-schema/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/react-motion/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/react-motion/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/react-motion/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/react-motion/favicon.ico -------------------------------------------------------------------------------- /chapter-06/react-motion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/react-motion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1", 16 | "react-motion": "^0.4.5" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Transition from './components/transition' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/components/transition.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Motion, spring } from 'react-motion' 3 | 4 | const Transition = () => ( 5 | 6 | {interpolatingStyle => ( 7 |

    Hello React

    8 | )} 9 |
    10 | ) 11 | 12 | export default Transition 13 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/react-motion/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/refs-dom/favicon.ico -------------------------------------------------------------------------------- /chapter-06/refs-dom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Focus from './components/focus' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/components/focus.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Focus extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.handleClick = this.handleClick.bind(this) 9 | } 10 | 11 | handleClick() { 12 | this.element.focus() 13 | } 14 | 15 | render() { 16 | return ( 17 |
    18 | (this.element = element)} /> 19 | 20 |
    21 | ) 22 | } 23 | 24 | } 25 | 26 | export default Focus 27 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/refs-dom/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/refs-instance/favicon.ico -------------------------------------------------------------------------------- /chapter-06/refs-instance/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Reset from './components/reset' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/components/input.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Input extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | value: '', 10 | } 11 | 12 | this.reset = this.reset.bind(this) 13 | this.handleChange = this.handleChange.bind(this) 14 | } 15 | 16 | reset() { 17 | this.setState({ 18 | value: '', 19 | }) 20 | } 21 | 22 | handleChange({ target }) { 23 | this.setState({ 24 | value: target.value, 25 | }) 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | ) 32 | } 33 | 34 | } 35 | 36 | export default Input 37 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/components/reset.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Input from './input' 3 | 4 | class Reset extends React.Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.handleClick = this.handleClick.bind(this) 10 | } 11 | 12 | handleClick(e) { 13 | e.preventDefault() 14 | 15 | this.element.reset() 16 | } 17 | 18 | render() { 19 | return ( 20 | 21 | (this.element = element)} /> 22 | 23 | 24 | ) 25 | } 26 | 27 | } 28 | 29 | export default Reset 30 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/refs-instance/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/svg/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/svg/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/svg/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/svg/favicon.ico -------------------------------------------------------------------------------- /chapter-06/svg/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/svg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/svg/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/svg/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import RedCircle from './components/red-circle' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/svg/src/components/circle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Circle = ({ x, y, radius, fill }) => ( 4 | 5 | 6 | 7 | ) 8 | 9 | Circle.propTypes = { 10 | x: React.PropTypes.number, 11 | y: React.PropTypes.number, 12 | radius: React.PropTypes.number, 13 | fill: React.PropTypes.string, 14 | } 15 | 16 | export default Circle 17 | -------------------------------------------------------------------------------- /chapter-06/svg/src/components/red-circle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Circle from './circle' 3 | 4 | const RedCircle = ({ x, y, radius }) => ( 5 | 6 | ) 7 | 8 | RedCircle.propTypes = { 9 | x: React.PropTypes.number, 10 | y: React.PropTypes.number, 11 | radius: React.PropTypes.number, 12 | } 13 | 14 | export default RedCircle 15 | -------------------------------------------------------------------------------- /chapter-06/svg/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/svg/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-06/svg/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-06/uncontrolled-components/favicon.ico -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Uncontrolled from './components/uncontrolled' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/src/components/uncontrolled.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Uncontrolled extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | firstName: '', 10 | lastName: '', 11 | } 12 | 13 | this.handleChange = this.handleChange.bind(this) 14 | this.handleSubmit = this.handleSubmit.bind(this) 15 | } 16 | 17 | handleChange({ target }) { 18 | this.setState({ 19 | [target.name]: target.value, 20 | }) 21 | } 22 | 23 | handleSubmit(e) { 24 | e.preventDefault() 25 | 26 | console.log(`${this.state.firstName} ${this.state.lastName}`) 27 | } 28 | 29 | render() { 30 | return ( 31 |
    32 | 33 | 34 | 35 |
    36 | ) 37 | } 38 | 39 | } 40 | 41 | export default Uncontrolled 42 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-06/uncontrolled-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-07/css-modules/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-07/css-modules/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /chapter-07/css-modules/index.css: -------------------------------------------------------------------------------- 1 | .background-red { 2 | background-color: #ff0000; 3 | } 4 | 5 | .button { 6 | composes: background-red; 7 | width: 320px; 8 | padding: 20px; 9 | border-radius: 5px; 10 | border: none; 11 | outline: none; 12 | } 13 | 14 | .button:hover { 15 | color: #fff; 16 | } 17 | 18 | .button:active { 19 | position: relative; 20 | top: 2px; 21 | } 22 | 23 | @media (max-width: 480px) { 24 | .button { 25 | width: 160px 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-07/css-modules/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import cssModules from 'react-css-modules' 4 | import styles from './index.css' 5 | 6 | const Button = () => 7 | 8 | const EnhancedButton = cssModules(Button, styles) 9 | 10 | ReactDOM.render(, document.body) 11 | -------------------------------------------------------------------------------- /chapter-07/css-modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "webpack-dev-server", 7 | "lint": "eslint ./" 8 | }, 9 | "devDependencies": { 10 | "eslint": "^2.9.0", 11 | "eslint-config-airbnb": "^9.0.1", 12 | "eslint-plugin-import": "^1.7.0", 13 | "eslint-plugin-jsx-a11y": "^1.2.0", 14 | "eslint-plugin-react": "^5.0.1", 15 | "babel-core": "^6.17.0", 16 | "babel-loader": "^6.2.5", 17 | "babel-preset-es2015": "^6.16.0", 18 | "babel-preset-react": "^6.16.0", 19 | "css-loader": "^0.25.0", 20 | "html-webpack-plugin": "^2.24.0", 21 | "style-loader": "^0.13.1", 22 | "webpack": "^1.13.2", 23 | "webpack-dev-server": "^1.16.2" 24 | }, 25 | "dependencies": { 26 | "react": "^15.3.2", 27 | "react-css-modules": "^3.7.10", 28 | "react-dom": "^15.3.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter-07/css-modules/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | 6 | module: { 7 | loaders: [ 8 | { 9 | test: /\.js$/, 10 | exclude: /(node_modules|bower_components)/, 11 | loader: 'babel', 12 | query: { 13 | presets: ['es2015', 'react'], 14 | }, 15 | }, 16 | { 17 | test: /\.css$/, 18 | loader: 'style!css?modules&localIdentName=[local]--[hash:base64:5]', 19 | }, 20 | ], 21 | }, 22 | 23 | plugins: [new HtmlWebpackPlugin()], 24 | } 25 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-07/inline-styles/favicon.ico -------------------------------------------------------------------------------- /chapter-07/inline-styles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import FontSize from './components/font-size' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/components/font-size.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class FontSize extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | value: 16, 10 | } 11 | 12 | this.handleChange = this.handleChange.bind(this) 13 | } 14 | 15 | handleChange({ target }) { 16 | this.setState({ 17 | value: Number(target.value), 18 | }) 19 | } 20 | 21 | render() { 22 | return ( 23 | 29 | ) 30 | } 31 | 32 | } 33 | 34 | export default FontSize 35 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-07/inline-styles/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-07/radium/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-07/radium/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-07/radium/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-07/radium/favicon.ico -------------------------------------------------------------------------------- /chapter-07/radium/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-07/radium/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "radium": "^0.18.1", 15 | "react": "^15.3.1", 16 | "react-dom": "^15.3.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-07/radium/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-07/radium/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import { StyleRoot } from 'radium' 6 | import Button from './components/button' 7 | 8 | class App extends Component { 9 | render() { 10 | return ( 11 | 12 |
    13 |
    14 | logo 15 |

    Welcome to React

    16 |
    17 |
    19 |
    20 | ) 21 | } 22 | } 23 | 24 | export default App 25 | -------------------------------------------------------------------------------- /chapter-07/radium/src/components/button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import radium from 'radium' 3 | 4 | const styles = { 5 | backgroundColor: '#ff0000', 6 | width: 320, 7 | padding: 20, 8 | borderRadius: 5, 9 | border: 'none', 10 | outline: 'none', 11 | ':hover': { 12 | color: '#fff', 13 | }, 14 | ':active': { 15 | position: 'relative', 16 | top: 2, 17 | }, 18 | '@media (max-width: 480px)': { 19 | width: 160, 20 | }, 21 | } 22 | 23 | const Button = () => 24 | 25 | export default radium(Button) 26 | -------------------------------------------------------------------------------- /chapter-07/radium/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-07/radium/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-07/radium/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-07/styled-components/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-07/styled-components/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-07/styled-components/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-07/styled-components/favicon.ico -------------------------------------------------------------------------------- /chapter-07/styled-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-07/styled-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1", 16 | "styled-components": "^1.0.8" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-07/styled-components/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-07/styled-components/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Button from './components/button' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-07/styled-components/src/components/button.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const Button = styled.button` 4 | background-color: #ff0000; 5 | width: 320px; 6 | padding: 20px; 7 | border-radius: 5px; 8 | border: none; 9 | outline: none; 10 | &:hover { 11 | color: #fff; 12 | } 13 | &:active { 14 | position: relative; 15 | top: 2px; 16 | } 17 | @media (max-width: 480px) { 18 | width: 160px; 19 | }` 20 | 21 | export default Button 22 | -------------------------------------------------------------------------------- /chapter-07/styled-components/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-07/styled-components/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "webpack", 7 | "start": "node ./dist/server", 8 | "lint": "eslint ./src" 9 | }, 10 | "devDependencies": { 11 | "babel-core": "^6.18.0", 12 | "babel-loader": "^6.2.6", 13 | "babel-preset-es2015": "^6.18.0", 14 | "babel-preset-react": "^6.16.0", 15 | "eslint": "^2.9.0", 16 | "eslint-config-airbnb": "^9.0.1", 17 | "eslint-plugin-import": "^1.7.0", 18 | "eslint-plugin-jsx-a11y": "^1.2.0", 19 | "eslint-plugin-react": "^5.0.1", 20 | "webpack": "^1.13.3", 21 | "webpack-node-externals": "^1.5.4" 22 | }, 23 | "dependencies": { 24 | "express": "^4.14.0", 25 | "isomorphic-fetch": "^2.2.1", 26 | "react": "^15.3.2", 27 | "react-dom": "^15.3.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const App = ({ gists }) => ( 4 |
      5 | {gists.map(gist => ( 6 |
    • {gist.description}
    • 7 | ))} 8 |
    9 | ) 10 | 11 | App.propTypes = { 12 | gists: React.PropTypes.array, 13 | } 14 | 15 | export default App 16 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/src/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './app' 4 | 5 | ReactDOM.render(, document.getElementById('app')) 6 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom/server' 4 | import fetch from 'isomorphic-fetch' 5 | import App from './app' 6 | import template from './template' 7 | 8 | const app = express() 9 | 10 | app.use(express.static('dist/public')) 11 | 12 | app.get('/', (req, res) => { 13 | fetch('https://api.github.com/users/gaearon/gists') 14 | .then(response => response.json()) 15 | .then(gists => { 16 | const body = ReactDOM.renderToString() 17 | const html = template(body, gists) 18 | 19 | res.send(html) 20 | }) 21 | }) 22 | 23 | app.listen(3000, () => { 24 | console.log('Listening on port 3000') 25 | }) 26 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/src/template.js: -------------------------------------------------------------------------------- 1 | export default (body, gists) => ` 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    ${body}
    9 | 10 | 11 | 12 | 13 | ` 14 | -------------------------------------------------------------------------------- /chapter-08/data-fetching/webpack.config.js: -------------------------------------------------------------------------------- 1 | const nodeExternals = require('webpack-node-externals') 2 | 3 | const loaders = [{ 4 | test: /\.js$/, 5 | exclude: /(node_modules|bower_components)/, 6 | loader: 'babel', 7 | query: { 8 | presets: ['es2015', 'react'], 9 | }, 10 | }] 11 | 12 | const client = { 13 | entry: './src/client.js', 14 | 15 | output: { 16 | path: './dist/public', 17 | filename: 'bundle.js', 18 | }, 19 | 20 | module: { loaders }, 21 | } 22 | 23 | const server = { 24 | entry: './src/server.js', 25 | 26 | output: { 27 | path: './dist', 28 | filename: 'server.js', 29 | }, 30 | 31 | module: { loaders }, 32 | 33 | target: 'node', 34 | 35 | externals: [nodeExternals()], 36 | } 37 | 38 | module.exports = [client, server] 39 | -------------------------------------------------------------------------------- /chapter-08/next/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "parserOptions": { 5 | "ecmaVersion": 8 6 | }, 7 | 8 | "rules": { 9 | "semi": [2, "never"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-08/next/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | -------------------------------------------------------------------------------- /chapter-08/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next", 7 | "lint": "eslint ./pages" 8 | }, 9 | "devDependencies": { 10 | "eslint": "^2.9.0", 11 | "eslint-config-airbnb": "^9.0.1", 12 | "eslint-plugin-import": "^1.7.0", 13 | "eslint-plugin-jsx-a11y": "^1.2.0", 14 | "eslint-plugin-react": "^5.0.1" 15 | }, 16 | "dependencies": { 17 | "isomorphic-fetch": "^2.2.1", 18 | "next": "^1.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-08/next/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import fetch from 'isomorphic-fetch' 3 | 4 | class App extends React.Component { 5 | 6 | static async getInitialProps() { 7 | const response = await fetch('https://api.github.com/users/gaearon/gists') 8 | const gists = await response.json() 9 | 10 | return { gists } 11 | } 12 | 13 | render() { 14 | return ( 15 |
      16 | {this.props.gists.map(gist => ( 17 |
    • {gist.description}
    • 18 | ))} 19 |
    20 | ) 21 | } 22 | 23 | } 24 | 25 | App.propTypes = { 26 | gists: React.PropTypes.array, 27 | } 28 | 29 | export default App 30 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "webpack", 7 | "start": "node ./dist/server", 8 | "lint": "eslint ./src" 9 | }, 10 | "devDependencies": { 11 | "babel-core": "^6.18.0", 12 | "babel-loader": "^6.2.6", 13 | "babel-preset-es2015": "^6.18.0", 14 | "babel-preset-react": "^6.16.0", 15 | "eslint": "^2.9.0", 16 | "eslint-config-airbnb": "^9.0.1", 17 | "eslint-plugin-import": "^1.7.0", 18 | "eslint-plugin-jsx-a11y": "^1.2.0", 19 | "eslint-plugin-react": "^5.0.1", 20 | "webpack": "^1.13.3", 21 | "webpack-node-externals": "^1.5.4" 22 | }, 23 | "dependencies": { 24 | "express": "^4.14.0", 25 | "react": "^15.3.2", 26 | "react-dom": "^15.3.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const App = () =>
    Hello React
    4 | 5 | export default App 6 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/src/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './app' 4 | 5 | ReactDOM.render(, document.getElementById('app')) 6 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom/server' 4 | import App from './app' 5 | import template from './template' 6 | 7 | const app = express() 8 | 9 | app.use(express.static('dist/public')) 10 | 11 | app.get('/', (req, res) => { 12 | const body = ReactDOM.renderToString() 13 | const html = template(body) 14 | res.send(html) 15 | }) 16 | 17 | app.listen(3000, () => { 18 | console.log('Listening on port 3000') 19 | }) 20 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/src/template.js: -------------------------------------------------------------------------------- 1 | export default body => ` 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    ${body}
    9 | 10 | 11 | 12 | ` 13 | -------------------------------------------------------------------------------- /chapter-08/server-side-rendering/webpack.config.js: -------------------------------------------------------------------------------- 1 | const nodeExternals = require('webpack-node-externals') 2 | 3 | const loaders = [{ 4 | test: /\.js$/, 5 | exclude: /(node_modules|bower_components)/, 6 | loader: 'babel', 7 | query: { 8 | presets: ['es2015', 'react'], 9 | }, 10 | }] 11 | 12 | const client = { 13 | entry: './src/client.js', 14 | 15 | output: { 16 | path: './dist/public', 17 | filename: 'bundle.js', 18 | }, 19 | 20 | module: { loaders }, 21 | } 22 | 23 | const server = { 24 | entry: './src/server.js', 25 | 26 | output: { 27 | path: './dist', 28 | filename: 'server.js', 29 | }, 30 | 31 | module: { loaders }, 32 | 33 | target: 'node', 34 | 35 | externals: [nodeExternals()], 36 | } 37 | 38 | module.exports = [client, server] 39 | -------------------------------------------------------------------------------- /chapter-09/constants-props/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "global-require": 0, 7 | "react/prefer-stateless-function": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chapter-09/constants-props/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-09/constants-props/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-09/constants-props/favicon.ico -------------------------------------------------------------------------------- /chapter-09/constants-props/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/constants-props/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-addons-perf": "^15.3.2", 12 | "react-scripts": "0.2.2", 13 | "why-did-you-update": "0.0.8" 14 | }, 15 | "dependencies": { 16 | "react": "^15.3.1", 17 | "react-dom": "^15.3.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "eject": "react-scripts eject", 23 | "lint": "eslint ./src" 24 | }, 25 | "eslintConfig": { 26 | "extends": "./node_modules/react-scripts/config/eslint.js" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | if (process.env.NODE_ENV !== 'production') { 6 | const { whyDidYouUpdate } = require('why-did-you-update') 7 | whyDidYouUpdate(React) 8 | } 9 | 10 | import List from './components/list' 11 | 12 | class App extends Component { 13 | render() { 14 | return ( 15 |
    16 |
    17 | logo 18 |

    Welcome to React

    19 |
    20 | 21 |
    22 | ) 23 | } 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/components/item.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Item extends React.PureComponent { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.handleClick = this.handleClick.bind(this) 9 | } 10 | 11 | handleClick() { 12 | this.props.onClick(this.props.item) 13 | } 14 | 15 | render() { 16 | return ( 17 |
  • 18 | {this.props.item} 19 |
  • 20 | ) 21 | } 22 | 23 | } 24 | 25 | export default Item 26 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Perf from 'react-addons-perf' 3 | import Item from './item' 4 | 5 | const statuses = ['open', 'close'] 6 | 7 | class List extends React.Component { 8 | 9 | constructor(props) { 10 | super(props) 11 | 12 | this.state = { 13 | items: ['foo', 'bar'], 14 | } 15 | 16 | this.handleClick = this.handleClick.bind(this) 17 | } 18 | 19 | componentWillUpdate() { 20 | Perf.start() 21 | } 22 | 23 | componentDidUpdate() { 24 | Perf.stop() 25 | Perf.printWasted() 26 | } 27 | 28 | handleClick() { 29 | const items = this.state.items.slice() 30 | items.unshift('baz') 31 | 32 | this.setState({ 33 | items, 34 | }) 35 | } 36 | 37 | render() { 38 | return ( 39 |
    40 |
      41 | {this.state.items.map(item => ( 42 | 48 | ))} 49 |
    50 | 51 |
    52 | ) 53 | } 54 | 55 | } 56 | 57 | export default List 58 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-09/constants-props/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "global-require": 0, 7 | "react/prefer-stateless-function": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-09/creating-functions/favicon.ico -------------------------------------------------------------------------------- /chapter-09/creating-functions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-addons-perf": "^15.3.2", 12 | "react-scripts": "0.2.2", 13 | "why-did-you-update": "0.0.8" 14 | }, 15 | "dependencies": { 16 | "react": "^15.3.1", 17 | "react-dom": "^15.3.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "eject": "react-scripts eject", 23 | "lint": "eslint ./src" 24 | }, 25 | "eslintConfig": { 26 | "extends": "./node_modules/react-scripts/config/eslint.js" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | if (process.env.NODE_ENV !== 'production') { 6 | const { whyDidYouUpdate } = require('why-did-you-update') 7 | whyDidYouUpdate(React) 8 | } 9 | 10 | import List from './components/list' 11 | 12 | class App extends Component { 13 | render() { 14 | return ( 15 |
    16 |
    17 | logo 18 |

    Welcome to React

    19 |
    20 | 21 |
    22 | ) 23 | } 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/components/item.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Item extends React.PureComponent { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.handleClick = this.handleClick.bind(this) 9 | } 10 | 11 | handleClick() { 12 | this.props.onClick(this.props.item) 13 | } 14 | 15 | render() { 16 | return ( 17 |
  • 18 | {this.props.item} 19 |
  • 20 | ) 21 | } 22 | 23 | } 24 | 25 | export default Item 26 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Perf from 'react-addons-perf' 3 | import Item from './item' 4 | 5 | class List extends React.Component { 6 | 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | items: ['foo', 'bar'], 12 | } 13 | 14 | this.handleClick = this.handleClick.bind(this) 15 | } 16 | 17 | componentWillUpdate() { 18 | Perf.start() 19 | } 20 | 21 | componentDidUpdate() { 22 | Perf.stop() 23 | Perf.printWasted() 24 | } 25 | 26 | handleClick() { 27 | const items = this.state.items.slice() 28 | items.unshift('baz') 29 | 30 | this.setState({ 31 | items, 32 | }) 33 | } 34 | 35 | render() { 36 | return ( 37 |
    38 |
      39 | {this.state.items.map(item => ( 40 | 41 | ))} 42 |
    43 | 44 |
    45 | ) 46 | } 47 | 48 | } 49 | 50 | export default List 51 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-09/creating-functions/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-09/good-design/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-09/good-design/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-09/good-design/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-09/good-design/favicon.ico -------------------------------------------------------------------------------- /chapter-09/good-design/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/good-design/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Todos from './components/todos' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/components/form.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Form extends React.PureComponent { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | value: '', 10 | } 11 | 12 | this.handleChange = this.handleChange.bind(this) 13 | } 14 | 15 | handleChange({ target }) { 16 | this.setState({ 17 | value: target.value, 18 | }) 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 | 25 | 26 |
    27 | ) 28 | } 29 | 30 | } 31 | 32 | export default Form 33 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class List extends React.PureComponent { 4 | 5 | render() { 6 | return ( 7 |
      8 | {this.props.items.map(item =>
    • {item}
    • )} 9 |
    10 | ) 11 | } 12 | 13 | } 14 | 15 | export default List 16 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/components/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from './list' 3 | import Form from './form' 4 | 5 | class Todos extends React.Component { 6 | 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | items: ['foo', 'bar'], 12 | } 13 | 14 | this.handleSubmit = this.handleSubmit.bind(this) 15 | } 16 | 17 | handleSubmit(value) { 18 | const items = this.state.items.slice() 19 | items.unshift(value) 20 | 21 | this.setState({ 22 | items, 23 | }) 24 | } 25 | 26 | render() { 27 | return ( 28 |
    29 | 30 |
    31 |
    32 | ) 33 | } 34 | } 35 | 36 | export default Todos 37 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-09/good-design/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-09/keys/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-09/keys/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-09/keys/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-09/keys/favicon.ico -------------------------------------------------------------------------------- /chapter-09/keys/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/keys/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-addons-perf": "^15.3.2", 12 | "react-scripts": "0.2.2" 13 | }, 14 | "dependencies": { 15 | "react": "^15.3.1", 16 | "react-dom": "^15.3.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-09/keys/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-09/keys/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import List from './components/list' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-09/keys/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Perf from 'react-addons-perf' 3 | 4 | class List extends React.Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | items: ['foo', 'bar'], 11 | } 12 | 13 | this.handleClick = this.handleClick.bind(this) 14 | } 15 | 16 | componentWillUpdate() { 17 | Perf.start() 18 | } 19 | 20 | componentDidUpdate() { 21 | Perf.stop() 22 | Perf.printOperations() 23 | } 24 | 25 | handleClick() { 26 | const items = this.state.items.slice() 27 | items.unshift('baz') 28 | 29 | this.setState({ 30 | items, 31 | }) 32 | } 33 | 34 | render() { 35 | return ( 36 |
    37 |
      38 | {this.state.items.map(item =>
    • {item}
    • )} 39 |
    40 | 41 |
    42 | ) 43 | } 44 | 45 | } 46 | 47 | export default List 48 | -------------------------------------------------------------------------------- /chapter-09/keys/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-09/keys/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-09/keys/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /chapter-10/enzyme/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-10/enzyme/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "env": { 5 | "jest": true 6 | }, 7 | 8 | "rules": { 9 | "semi": [2, "never"], 10 | "react/prefer-stateless-function": 0 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-10/enzyme/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /chapter-10/enzyme/button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Button extends React.Component { 4 | 5 | render() { 6 | return ( 7 | 10 | ) 11 | } 12 | 13 | } 14 | 15 | Button.propTypes = { 16 | onClick: React.PropTypes.func, 17 | text: React.PropTypes.string, 18 | } 19 | 20 | export default Button 21 | -------------------------------------------------------------------------------- /chapter-10/enzyme/button.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import Button from './button' 4 | 5 | test('renders with text', () => { 6 | const text = 'text' 7 | const button = shallow( 10 | ) 11 | } 12 | 13 | } 14 | 15 | Button.propTypes = { 16 | onClick: React.PropTypes.func, 17 | text: React.PropTypes.string, 18 | } 19 | 20 | export default Button 21 | -------------------------------------------------------------------------------- /chapter-10/jest/button.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TestUtils from 'react-addons-test-utils' 3 | import Button from './button' 4 | 5 | test('renders with text', () => { 6 | const text = 'text' 7 | 8 | const renderer = TestUtils.createRenderer() 9 | renderer.render( 10 | ) 11 | } 12 | 13 | } 14 | 15 | Button.propTypes = { 16 | onClick: React.PropTypes.func, 17 | text: React.PropTypes.string, 18 | } 19 | 20 | export default Button 21 | -------------------------------------------------------------------------------- /chapter-10/mocha/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "mocha --compilers js:babel-register", 7 | "lint": "eslint ./" 8 | }, 9 | "devDependencies": { 10 | "babel-preset-es2015": "^6.18.0", 11 | "babel-preset-react": "^6.16.0", 12 | "babel-register": "^6.18.0", 13 | "chai": "^3.5.0", 14 | "chai-spies": "^0.7.1", 15 | "eslint": "^2.9.0", 16 | "eslint-config-airbnb": "^9.0.1", 17 | "eslint-plugin-import": "^1.7.0", 18 | "eslint-plugin-jsx-a11y": "^1.2.0", 19 | "eslint-plugin-react": "^5.0.1", 20 | "jsdom": "^9.8.3", 21 | "mocha": "^3.1.2", 22 | "react-addons-test-utils": "^15.3.2", 23 | "sinon": "^1.17.6" 24 | }, 25 | "dependencies": { 26 | "react": "^15.3.2", 27 | "react-dom": "^15.3.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter-10/mocha/test/button.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai' 2 | import spies from 'chai-spies' 3 | import { jsdom } from 'jsdom' 4 | import React from 'react' 5 | import TestUtils from 'react-addons-test-utils' 6 | import Button from '../button' 7 | 8 | chai.use(spies) 9 | 10 | const { expect, spy } = chai 11 | 12 | global.document = jsdom('') 13 | global.window = document.defaultView 14 | 15 | describe('Button', () => { 16 | it('renders with text', () => { 17 | const text = 'text' 18 | 19 | const renderer = TestUtils.createRenderer() 20 | renderer.render( 45 | 46 | ) 47 | } 48 | 49 | } 50 | 51 | Controlled.propTypes = { 52 | onSubmit: React.PropTypes.func, 53 | } 54 | 55 | export default Controlled 56 | -------------------------------------------------------------------------------- /chapter-10/page-object/form.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import Controlled from './form' 4 | import Page from './page' 5 | 6 | test('submits the form', () => { 7 | const onSubmit = jest.fn() 8 | const wrapper = shallow() 9 | 10 | const firstName = wrapper.find('[name="firstName"]') 11 | firstName.simulate('change', { target: { name: 'firstName', value: 'Christopher' } }) 12 | 13 | const lastName = wrapper.find('[name="lastName"]') 14 | lastName.simulate('change', { target: { name: 'lastName', value: 'Chedeau' } }) 15 | 16 | const form = wrapper.find('form') 17 | form.simulate('submit', { preventDefault: () => {} }) 18 | 19 | expect(onSubmit).toHaveBeenCalledWith('Christopher Chedeau') 20 | }) 21 | 22 | test('submits the form with the page object', () => { 23 | const onSubmit = jest.fn() 24 | const wrapper = shallow() 25 | 26 | const page = new Page(wrapper) 27 | page.fill('firstName', 'Christopher') 28 | page.fill('lastName', 'Chedeau') 29 | page.submit() 30 | 31 | expect(onSubmit).toHaveBeenCalledWith('Christopher Chedeau') 32 | }) 33 | -------------------------------------------------------------------------------- /chapter-10/page-object/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest", 7 | "lint": "eslint ./" 8 | }, 9 | "devDependencies": { 10 | "babel-jest": "^17.0.0", 11 | "babel-preset-es2015": "^6.18.0", 12 | "babel-preset-react": "^6.16.0", 13 | "enzyme": "^2.6.0", 14 | "eslint": "^2.9.0", 15 | "eslint-config-airbnb": "^9.0.1", 16 | "eslint-plugin-import": "^1.7.0", 17 | "eslint-plugin-jsx-a11y": "^1.2.0", 18 | "eslint-plugin-react": "^5.0.1", 19 | "jest": "^17.0.0", 20 | "react-addons-test-utils": "^15.3.2" 21 | }, 22 | "dependencies": { 23 | "react": "^15.3.2", 24 | "react-dom": "^15.3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-10/page-object/page.js: -------------------------------------------------------------------------------- 1 | class Page { 2 | 3 | constructor(wrapper) { 4 | this.wrapper = wrapper 5 | } 6 | 7 | fill(name, value) { 8 | const field = this.wrapper.find(`[name="${name}"]`) 9 | field.simulate('change', { target: { name, value } }) 10 | } 11 | 12 | submit() { 13 | const form = this.wrapper.find('form') 14 | form.simulate('submit', { preventDefault() {} }) 15 | } 16 | 17 | } 18 | 19 | export default Page 20 | -------------------------------------------------------------------------------- /chapter-10/real-world/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"], 3 | 4 | "plugins": ["transform-class-properties"] 5 | } 6 | -------------------------------------------------------------------------------- /chapter-10/real-world/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /chapter-10/real-world/TodoTextInput-snapshot.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderer from 'react-test-renderer' 3 | import TodoTextInput from './TodoTextInput' 4 | 5 | test('snapshots are awesome', () => { 6 | const component = renderer.create( {}} />) 7 | const tree = component.toJSON() 8 | 9 | expect(tree).toMatchSnapshot() 10 | }) 11 | -------------------------------------------------------------------------------- /chapter-10/real-world/TodoTextInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import classnames from 'classnames' 3 | 4 | export default class TodoTextInput extends Component { 5 | static propTypes = { 6 | onSave: PropTypes.func.isRequired, 7 | text: PropTypes.string, 8 | placeholder: PropTypes.string, 9 | editing: PropTypes.bool, 10 | newTodo: PropTypes.bool 11 | } 12 | 13 | state = { 14 | text: this.props.text || '' 15 | } 16 | 17 | handleSubmit = e => { 18 | const text = e.target.value.trim() 19 | if (e.which === 13) { 20 | this.props.onSave(text) 21 | if (this.props.newTodo) { 22 | this.setState({ text: '' }) 23 | } 24 | } 25 | } 26 | 27 | handleChange = e => { 28 | this.setState({ text: e.target.value }) 29 | } 30 | 31 | handleBlur = e => { 32 | if (!this.props.newTodo) { 33 | this.props.onSave(e.target.value) 34 | } 35 | } 36 | 37 | render() { 38 | return ( 39 | 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /chapter-10/real-world/TodoTextInput.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import TodoTextInput from './TodoTextInput' 4 | 5 | const noop = () => {} 6 | 7 | test('sets the text prop as value', () => { 8 | const text = 'text' 9 | const wrapper = shallow() 10 | 11 | expect(wrapper.prop('value')).toBe(text) 12 | }) 13 | 14 | test('uses the placeholder prop', () => { 15 | const placeholder = 'placeholder' 16 | const wrapper = shallow() 17 | 18 | expect(wrapper.prop('placeholder')).toBe(placeholder) 19 | }) 20 | 21 | test('applies the right class names', () => { 22 | const wrapper = shallow() 23 | 24 | expect(wrapper.hasClass('edit new-todo')).toBe(true) 25 | }) 26 | 27 | test('fires onSave on enter', () => { 28 | const onSave = jest.fn() 29 | const value = 'value' 30 | const wrapper = shallow() 31 | 32 | wrapper.simulate('keydown', { target: { value }, which: 13 }) 33 | 34 | expect(onSave).toHaveBeenCalledWith(value) 35 | }) 36 | 37 | test('does not fire onSave on key down', () => { 38 | const onSave = jest.fn() 39 | const wrapper = shallow() 40 | 41 | wrapper.simulate('keydown', { target: { value: '' } }) 42 | 43 | expect(onSave).not.toBeCalled() 44 | }) 45 | 46 | test('clears the value after save if new', () => { 47 | const value = 'value' 48 | const wrapper = shallow() 49 | 50 | wrapper.simulate('keydown', { target: { value }, which: 13 }) 51 | 52 | expect(wrapper.prop('value')).toBe('') 53 | }) 54 | 55 | test('updates the text on change', () => { 56 | const value = 'value' 57 | const wrapper = shallow() 58 | 59 | wrapper.simulate('change', { target: { value } }) 60 | 61 | expect(wrapper.prop('value')).toBe(value) 62 | }) 63 | 64 | test('fires onSave on blur if not new', () => { 65 | const onSave = jest.fn() 66 | const value = 'value' 67 | const wrapper = shallow() 68 | 69 | wrapper.simulate('blur', { target: { value } }) 70 | 71 | expect(onSave).toHaveBeenCalledWith(value) 72 | }) 73 | 74 | test('does not fire onSave on blur if new', () => { 75 | const onSave = jest.fn() 76 | const wrapper = shallow() 77 | 78 | wrapper.simulate('blur') 79 | 80 | expect(onSave).not.toBeCalled() 81 | }) 82 | -------------------------------------------------------------------------------- /chapter-10/real-world/__snapshots__/TodoTextInput-snapshot.spec.js.snap: -------------------------------------------------------------------------------- 1 | exports[`test snapshots are awesome 1`] = ` 2 | 11 | `; 12 | -------------------------------------------------------------------------------- /chapter-10/real-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "jest": { 9 | "collectCoverage": true 10 | }, 11 | "devDependencies": { 12 | "babel-jest": "^17.0.0", 13 | "babel-plugin-transform-class-properties": "^6.18.0", 14 | "babel-preset-es2015": "^6.18.0", 15 | "babel-preset-react": "^6.16.0", 16 | "enzyme": "^2.6.0", 17 | "jest": "^17.0.0", 18 | "react-addons-test-utils": "^15.3.2", 19 | "react-test-renderer": "^15.3.2" 20 | }, 21 | "dependencies": { 22 | "classnames": "^2.2.5", 23 | "react": "^15.3.2", 24 | "react-dom": "^15.3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-11/index-as-key/favicon.ico -------------------------------------------------------------------------------- /chapter-11/index-as-key/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-addons-perf": "^15.3.2", 12 | "react-scripts": "0.2.2" 13 | }, 14 | "dependencies": { 15 | "react": "^15.3.1", 16 | "react-dom": "^15.3.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "eject": "react-scripts eject", 22 | "lint": "eslint ./src" 23 | }, 24 | "eslintConfig": { 25 | "extends": "./node_modules/react-scripts/config/eslint.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import List from './components/list' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Perf from 'react-addons-perf' 3 | 4 | class List extends React.PureComponent { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | items: ['foo', 'bar'], 11 | } 12 | 13 | this.handleClick = this.handleClick.bind(this) 14 | } 15 | 16 | componentWillUpdate() { 17 | Perf.start() 18 | } 19 | 20 | componentDidUpdate() { 21 | Perf.stop() 22 | Perf.printOperations() 23 | } 24 | 25 | handleClick() { 26 | const items = this.state.items.slice() 27 | items.unshift('baz') 28 | 29 | this.setState({ 30 | items, 31 | }) 32 | } 33 | 34 | render() { 35 | return ( 36 |
    37 |
      38 | {this.state.items.map((item, index) => ( 39 |
    • 40 | {item} 41 | 42 |
    • 43 | ))} 44 |
    45 | 46 |
    47 | ) 48 | } 49 | 50 | } 51 | 52 | export default List 53 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-11/index-as-key/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-11/initializing-state/favicon.ico -------------------------------------------------------------------------------- /chapter-11/initializing-state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Counter from './components/counter' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
    11 |
    12 | logo 13 |

    Welcome to React

    14 |
    15 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/src/components/counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Counter extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | count: props.initialCount, 10 | } 11 | 12 | this.handleClick = this.handleClick.bind(this) 13 | } 14 | 15 | handleClick() { 16 | this.setState({ 17 | count: this.state.count + 1, 18 | }) 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 | {this.state.count} 25 | 26 |
    27 | ) 28 | } 29 | 30 | } 31 | 32 | Counter.propTypes = { 33 | initialCount: React.PropTypes.number, 34 | } 35 | 36 | export default Counter 37 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-11/initializing-state/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | 4 | "rules": { 5 | "semi": [2, "never"], 6 | "react/prefer-stateless-function": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/chapter-11/mutating-state/favicon.ico -------------------------------------------------------------------------------- /chapter-11/mutating-state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-design-patterns-and-best-practices", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "eslint": "^2.9.0", 7 | "eslint-config-airbnb": "^9.0.1", 8 | "eslint-plugin-import": "^1.7.0", 9 | "eslint-plugin-jsx-a11y": "^1.2.0", 10 | "eslint-plugin-react": "^5.0.1", 11 | "react-scripts": "0.2.2" 12 | }, 13 | "dependencies": { 14 | "react": "^15.3.1", 15 | "react-dom": "^15.3.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "lint": "eslint ./src" 22 | }, 23 | "eslintConfig": { 24 | "extends": "./node_modules/react-scripts/config/eslint.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | margin-bottom: 20px; 16 | } 17 | 18 | @keyframes App-logo-spin { 19 | from { transform: rotate(0deg); } 20 | to { transform: rotate(360deg); } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | import Counter from './components/counter' 6 | import List from './components/list' 7 | 8 | class App extends Component { 9 | render() { 10 | return ( 11 |
    12 |
    13 | logo 14 |

    Welcome to React

    15 |
    16 | 17 | 18 |
    19 | ) 20 | } 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/components/counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Counter extends React.Component { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | count: props.initialCount, 10 | } 11 | 12 | this.handleClick = this.handleClick.bind(this) 13 | } 14 | 15 | handleClick() { 16 | this.state.count++ 17 | } 18 | 19 | render() { 20 | return ( 21 |
    22 | {this.state.count} 23 | 24 | 25 |
    26 | ) 27 | } 28 | 29 | } 30 | 31 | Counter.propTypes = { 32 | initialCount: React.PropTypes.number, 33 | } 34 | 35 | export default Counter 36 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/components/list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class List extends React.PureComponent { 4 | 5 | constructor(props) { 6 | super(props) 7 | 8 | this.state = { 9 | items: ['foo', 'bar'], 10 | } 11 | 12 | this.handleClick = this.handleClick.bind(this) 13 | } 14 | 15 | handleClick() { 16 | this.setState({ 17 | items: this.state.items.concat('baz'), 18 | }) 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 | {this.state.items.length} 25 | 26 |
    27 | ) 28 | } 29 | 30 | } 31 | 32 | export default List 33 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-11/mutating-state/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ) 10 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicheleBertoli/react-design-patterns-and-best-practices/78fb849ff46a5b841ce4821f267b0ccfc67060ce/cover.jpg --------------------------------------------------------------------------------