The Ultimate Cure for Your Next Hangover

by juno theme
October 13, 2016
Fullstack React The Complete Book on ReactJS and Friends Anthony Accomazzo, Ari Lerner, David Guttman, Nate Murray, Clay Allsopp and Tyler McGinnis © 2015 - 2016 Fullstack.io Contents Book Revision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Prerelease . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Chat With The Community! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Be notified of updates via Twitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 We’d love to hear from you! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Your first React Web Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Building Product Hunt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Setting up your development environment . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Code editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Node.js and npm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Special instruction for Windows users . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Ensure IIS is installed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Previewing the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Prepare the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Our first component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 React.createClass() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 JSX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 The developer console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Babel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 ReactDOM.render() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Our second component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Making Product data-driven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 The data model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Using props . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Rendering multiple products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 React the vote (your app’s first interaction) . . . . . . . . . . . . . . . . . . . . . . . . . 25 Propagating the event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Using state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Setting state with this.setState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 CONTENTS Updating state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Congratulations! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 A time-logging app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Previewing the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Prepare the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Breaking the app into components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 The steps for building React apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Step 2: Build a static version of the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 TimersDashboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 EditableTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 TimerForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 ToggleableTimerForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Render the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Step 3: Determine what should be stateful . . . . . . . . . . . . . . . . . . . . . . . . . . 57 State criteria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Applying the criteria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Step 4: Determine in which component each piece of state should live . . . . . . . . . . . 59 The list of timers and properties of each timer . . . . . . . . . . . . . . . . . . . . . . 59 Whether or not the edit form of a timer is open . . . . . . . . . . . . . . . . . . . . . 60 Visibility of the create form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Step 5: Hard-code initial states . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Adding state to TimersDashboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Receiving props in EditableTimerList . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Props vs. state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Adding state to EditableTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Timer and TimerForm remain stateless . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Adding state to ToggleableTimerForm . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Step 6: Add inverse data flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 TimerForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 ToggleableTimerForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 TimersDashboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Updating timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Adding editability to Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Updating EditableTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Updating EditableTimerList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Defining onEditFormSubmit() in TimersDashboard . . . . . . . . . . . . . . . . . . . 74 Deleting timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Adding the event handler to Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 CONTENTS Routing through EditableTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Routing through EditableTimerList . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Implementing the delete function in TimersDashboard . . . . . . . . . . . . . . . . . . 79 Adding timing functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Adding a forceUpdate() interval to Timer . . . . . . . . . . . . . . . . . . . . . . . . 82 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Add start and stop functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Add timer action events to Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Create TimerActionButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 Run the events through EditableTimer and EditableTimerList . . . . . . . . . . . . 85 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Methodology review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Components & Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 server.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 The Server API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 text/html endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 JSON endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Playing with the API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Loading state from the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Fetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 Sending starts and stops to the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Sending creates, updates, and deletes to the server . . . . . . . . . . . . . . . . . . . . . . 105 Give it a spin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Next up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 JSX and the Virtual DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 React Uses a Virtual DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Why Not Modify the Actual DOM? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 What is a Virtual DOM? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Virtual DOM Pieces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 ReactElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Experimenting with ReactElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Rendering Our ReactElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Adding Text (with children) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 ReactDOM.render() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 JSX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 JSX Creates Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 JSX Attribute Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 CONTENTS JSX Conditional Child Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 JSX Boolean Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 JSX Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 JSX Spread Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 JSX Gotchas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 JSX Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Advanced Component Configuration with props, state, and children . . . . . . . . . . . 124 Intro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 ReactComponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Creating ReactComponents - createClass or ES6 Classes . . . . . . . . . . . . . . . . 125 render() Returns a ReactElement Tree . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Getting Data into render() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 props are the parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 PropTypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Default props with getDefaultProps() . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Using state: Building a Custom Radio Button . . . . . . . . . . . . . . . . . . . . . . 135 getInitialState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Thinking About State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 Stateless Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 Switching to Stateless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Stateless Encourages Reuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Talking to Children Components with props.children . . . . . . . . . . . . . . . . . . . 146 React.Children.map() & React.Children.forEach() . . . . . . . . . . . . . . . . . 149 React.Children.toArray() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 ReactComponent Static Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Forms 101 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 The Basic Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Events and Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 Back to the Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 Text Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Accessing User Input With refs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 Using User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Uncontrolled vs. Controlled Components . . . . . . . . . . . . . . . . . . . . . . . . . 165 Accessing User Input With state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 CONTENTS Multiple Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 On Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Adding Validation to Our App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Creating the Field Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 Using our new Field Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Remote Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Building the Custom Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Adding CourseSelect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Separation of View and State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Async Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 Form Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Connect the Store . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 Form Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 formsy-react . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 react-input-enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 tcomb-form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 winterfell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 react-redux-form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 Using Webpack with create-react-app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 JavaScript modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 create-react-app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 Exploring create-react-app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 public/index.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 package.json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 src/ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 index.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Booting the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 Webpack basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 Making modifications to the sample app . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Hot reloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Auto-reloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Creating a production build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 Ejecting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 Buckle up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 Using create-react-app with an API server . . . . . . . . . . . . . . . . . . . . . . . . . . 243 The completed app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 How the app is organized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 The server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 Concurrently . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 Using the Webpack development proxy . . . . . . . . . . . . . . . . . . . . . . . . . . 252 CONTENTS Webpack at large . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 When to use Webpack/create-react-app . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Writing tests without a framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Preparing Modash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Writing the first spec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 The assertEqual() function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 What is Jest? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 Using Jest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 expect() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 The first Jest test for Modash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 The other truncate() spec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 The rest of the specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 Testing strategies for React applications . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 Integration vs Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 Shallow rendering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Enzyme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Testing a basic React component with Enzyme . . . . . . . . . . . . . . . . . . . . . . . 276 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 The App component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 The first spec for App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 More assertions for App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 Using beforeEach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 Simulating a change . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 Clearing the input field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 Simulating a form submission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 Writing tests for the food lookup app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 FoodSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 Exploring FoodSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Writing FoodSearch.test.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 In initial state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 A user has typed a value into the search field . . . . . . . . . . . . . . . . . . . . . . . 316 Mocking with Jest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Mocking Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 The API returns results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 The user clicks on a food item . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 The API returns empty result set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 What’s in a URL? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 React Router’s core components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 CONTENTS Building the components of react-router . . . . . . . . . . . . . . . . . . . . . . . . . . 348 The completed app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 Building Match . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 Building Link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 Building Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Building Redirect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 Using react-router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 More Match . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 Using Miss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 Dynamic routing with React Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 The completed app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 The server’s API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 Starting point of the app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 Using URL params . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 Propagating pathnames as props . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Dynamic menu items with Link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404 Supporting authenticated routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 The Client library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 Implementing login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 MatchWhenLoggedIn, a higher-order component . . . . . . . . . . . . . . . . . . . . . 416 Redirect state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420 Recap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Intro to Flux and Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Why Flux? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Flux is a Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Flux overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 Flux implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 Redux’s key ideas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 Building a counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 The counter’s actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428 Incrementing the counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 Decrementing the counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 Supporting additional parameters on actions . . . . . . . . . . . . . . . . . . . . . . . 432 Building the store . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 The core of Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 Next up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 The beginnings of a chat app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 CONTENTS Previewing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 Building the reducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 Initializing state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 Handling the ADD_MESSAGE action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 Handling the DELETE_MESSAGE action . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 Subscribing to the store . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 createStore() in full . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452 Connecting Redux to React . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 Using store.getState() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 Using store.subscribe() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 Using store.dispatch() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456 The app’s components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456 Preparing app.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 The App component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458 The MessageInput component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 The MessageView component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 ReactDOM.render() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 Next up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464 Intermediate Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Using createStore() from the redux library . . . . . . . . . . . . . . . . . . . . . . . . 466 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467 Representing messages as objects in state . . . . . . . . . . . . . . . . . . . . . . . . . . 467 Updating ADD_MESSAGE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468 Updating DELETE_MESSAGE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 Updating the React components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 Introducing threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 Supporting threads in initialState . . . . . . . . . . . . . . . . . . . . . . . . . . . 475 Supporting threads in the React components . . . . . . . . . . . . . . . . . . . . . . . 477 Modifying App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478 Turning MessageView into Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480 Adding the ThreadTabs component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 Updating App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 Creating ThreadTabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 Supporting threads in the reducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 Updating ADD_MESSAGE in the reducer . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 Updating the MessageInput component . . . . . . . . . . . . . . . . . . . . . . . . . . 491 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492 CONTENTS Updating DELETE_MESSAGE in the reducer . . . . . . . . . . . . . . . . . . . . . . . . . 494 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 Adding the action OPEN_THREAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 The action object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 Modifying the reducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 Dispatching from ThreadTabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500 Breaking up the reducer function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 A new reducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 Updating threadsReducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508 Adding messagesReducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508 Modifying the ADD_MESSAGE action handler . . . . . . . . . . . . . . . . . . . . . . . . 509 Creating messagesReducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 Modifying the DELETE_MESSAGE action handler . . . . . . . . . . . . . . . . . . . . . . 511 Adding DELETE_MESSAGE to messagesReducer() . . . . . . . . . . . . . . . . . . . . . 514 Defining the initial state in the reducers . . . . . . . . . . . . . . . . . . . . . . . . . . . 515 Initial state in reducer() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516 Adding initial state to activeThreadIdReducer() . . . . . . . . . . . . . . . . . . . . 517 Adding initial state to threadsReducer() . . . . . . . . . . . . . . . . . . . . . . . . . 518 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519 Using combineReducers() from redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Next up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Using Presentational and Container Components with Redux . . . . . . . . . . . . . . . . 522 Presentational and container components . . . . . . . . . . . . . . . . . . . . . . . . . 522 Splitting up ThreadTabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524 Splitting up Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529 Removing store from App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 Try it out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 Generating containers with react-redux . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 The Provider component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 Wrapping App in Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 Using connect() to generate ThreadTabs . . . . . . . . . . . . . . . . . . . . . . . . . 540 Using connect() to generate ThreadDisplay . . . . . . . . . . . . . . . . . . . . . . . 543 Action creators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 Asynchronicity and server communication . . . . . . . . . . . . . . . . . . . . . . . . 553 Using GraphQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554 Your First GraphQL Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554 GraphQL Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 GraphQL vs. REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 CONTENTS GraphQL vs. SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558 Relay and GraphQL Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558 Chapter Preview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 Consuming GraphQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 Exploring With GraphiQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 GraphQL Syntax 101 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568 Complex Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572 Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 Fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 Exploring a Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 Graph Nodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579 Viewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581 Graph Connections and Edges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582 Mutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586 GraphQL With JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587 GraphQL With React . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589 Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590 GraphQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591 Writing a GraphQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591 Special setup for Windows users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591 Game Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592 Express HTTP Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592 Adding First GraphQL Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 Adding GraphiQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 Introspection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600 Mutation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601 Rich Schemas and SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604 Setting Up The Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605 Schema Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 Object and Scalar Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Performance: Look-Ahead Optimizations . . . . . . . . . . . . . . . . . . . . . . . . . 618 Lists Continued . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621 Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631 Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 Rich Mutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637 Relay and GraphQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640 Performance: N+1 Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645 CONTENTS Appendix A: PropTypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647 Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647 string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648 number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648 boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649 function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650 object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650 object shape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 multiple types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 instanceOf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 array of type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653 node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654 element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654 any type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656 Optional & required props . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656 custom validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 Appendix B: Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 Curl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 A GET Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 A POST Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 Chrome “Copy as cURL” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659 More Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660 Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 19 - 2016-12-20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 18 - 2016-11-22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 17 - 2016-11-04 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 16 - 2016-10-12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 15 - 2016-10-05 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 14 - 2016-08-26 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 13 - 2016-08-02 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 12 - 2016-07-26 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 Revision 11 - 2016-07-08 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 10 - 2016-06-24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 9 - 2016-06-21 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 8 - 2016-06-02 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 7 - 2016-05-13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 6 - 2016-05-13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 5 - 2016-04-25 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 4 - 2016-04-22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Revision 3 - 2016-04-08 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 CONTENTS Revision 2 - 2016-03-16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 Revision 1 - 2016-02-14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 CONTENTS 1 Book Revision Revision 19 - Supports React 15.4.1 (2016-12-20) Prerelease This book is a prerelease version and a work-in-progress. Bug Reports If you’d like to report any bugs, typos, or suggestions just email us at: react@fullstack.io¹. Chat With The Community! We’re experimenting with a community chat room for this book using Gitter. If you’d like to hang out with other people learning React, come join us on Gitter²! Be notified of updates via Twitter If you’d like to be notified of updates to the book on Twitter, follow @fullstackio³ We’d love to hear from you! Did you like the book? Did you find it helpful? We’d love to add your face to our list of testimonials on the website! Email us at: react@fullstack.io⁴. ¹mailto:react@fullstack.io?Subject=Fullstack%20React%20book%20feedback ²https://gitter.im/fullstackreact/fullstackreact ³https://twitter.com/fullstackio ⁴mailto:react@fullstack.io?Subject=react%202%20testimonial Your first React Web Application Building Product Hunt In this chapter, you’re going to get a crash course on React by building a simple voting application inspired by Product Hunt⁵. You’ll become familiar with how React approaches front-end development and all of the fundamentals necessary to build an interactive React app from start to finish. Thanks to React’s core simplicity, by the end of the chapter you’ll already be well on your way to writing a variety of fast, dynamic interfaces. We’ll focus on getting our React app up and running fast. We take a deeper look at concepts covered in this section throughout the course. Setting up your development environment Code editor As you’ll be writing code throughout this course, you’ll need to make sure you have a code editor you’re comfortable working with. If you don’t already have a preferred editor, we recommend installing Atom⁶ or Sublime Text⁷. Node.js and npm For all the projects in this course, we’ll need to make sure we have a working Node.js⁸ development environment along with npm. There are a couple different ways you can install Node.js so please refer to the Node.js website for detailed information: https://nodejs.org/download/⁹ If you’re on a Mac, your best bet is to install Node.js directly from the Node.js website instead of through another package manager (like Homebrew). Installing Node.js via Homebrew is known to cause some issues. The Node Package Manager (npm for short) is installed as a part of Node.js. To check if npm is available as a part of our development environment, we can open a terminal window and type: ⁵http://producthunt.com ⁶http://atom.io ⁷https://www.sublimetext.com/ ⁸http://nodejs.org ⁹https://nodejs.org/download/ Your first React Web Application 2 $ npm -v If a version number is not printed out and you receive an error, make sure to download a Node.js installer that includes npm. Browser Lastly, we highly recommend using the Google Chrome Web Browser¹⁰ to develop React apps. We’ll use the Chrome developer toolkit throughout this course. To follow along with our development and debugging we recommend downloading it now. Special instruction for Windows users In the current pre-release version of this book, we will be using Unix/Mac commands in the terminal. Most of these commands, like ls and cd, are cross-platform. However, sometimes these commands are Unix/Mac-specific or contain Unix/Mac-specific flags (like ls -1p). As a result, be alert that you may have to occasionally determine the equivalent of a Unix/Mac command for your shell. Fortunately, the amount of work we do in the terminal is minimal and you will not encounter this issue often. We have tested all the code in PowerShell. In the future, when we add Windows commands to the text, we will add the commands for PowerShell. Therefore, we recommend that you use PowerShell as well. At the moment, our terminal examples use Unix/Mac commands. We are updating the book soon to include Windows-specific commands where they diverge. In previous versions of the book, we recommended that you use Cygwin for your development environment. Due to increased support for Node.js in Windows, we no longer recommend Cygwin. Ensure IIS is installed If you’re on a Windows machine and have yet to do any web development on it, you may need to install IIS (Internet Information Services) in order to run web servers locally. See this tutorial¹¹ for installing IIS. ¹⁰https://www.google.com/chrome/ ¹¹http://www.howtogeek.com/112455/how-to-install-iis-8-on-windows-8/ Your first React Web Application 3 Getting started Sample Code All the code examples you find in each chapter are available in the code package that came with the book. In that code package you’ll find completed versions of each app as well as boilerplates that we will use to build those apps together. Each chapter provides detailed instruction on how to follow along on your own. While coding along with the book is not necessary, we highly recommend doing so. Playing around with examples and sample code will help solidify and strengthen concepts. Previewing the application We’ll be building a basic React app that will allow us to touch on React’s most important concepts at a high-level before diving into them in subsequent sections. Let’s begin by taking a look at a working implementation of the app. Open up the sample code that came with the course, changing to the voting_app/ directory in the terminal: $ cd voting_app/ If you’re not familiar with cd, it stands for “change directory.” If you’re on a Mac, do the following to open terminal and change to the proper directory: 1. Open up /Applications/Utilities/Terminal.app. 2. Type cd, without hitting enter. 3. Tap the spacebar. 4. In the Finder, drag the voting_app/ folder on to your terminal window. 5. Hit Enter. Your terminal is now in the proper directory. Throughout the book, a codeblock starting with a $ signifies a command to be run in your terminal. First, we’ll need to use npm to install all our dependencies: Your first React Web Application 4 $ npm install With our dependencies installed, we can boot the server using the npm run script server: $ npm run server Heading to our browser, we can view the running application at the URL: http://localhost:3000¹². Completed version of the app ¹²http://localhost:3000 Your first React Web Application 5 Mac users can click on links inside of terminal. Just hold command and double-click on the link: Clicking a link in the console This demo app is a site like Product Hunt¹³ or Reddit¹⁴. These sites have lists of links that users can vote on. In our app we can up-vote products and all products are sorted, instantaneously, by number of votes. The app we build in this section has a slightly different style than the completed version we just looked at. This is because the completed version has some additional HTML structure that’s purely for aesthetics. Feel free to use the HTML in app-complete.js to style your component after completing this section. To quit a running Node server, hit CTRL+C. Prepare the app In the terminal, run ls -1p to see the project’s layout: $ ls -1p ¹³http://producthunt.com ¹⁴http://reddit.com Your first React Web Application 6 README.md app-complete.js app.js data.js images/ index.html node_modules/ package.json style.css vendor/ If you’re using Windows, this is an example of a command that is not cross-platform. In PowerShell, the ls command with no flags is already nicely displayed, so just use ls as opposed to ls -1p wherever you see that command. We’ll be working with index.html and app.js for this project. app-complete.js is the completed application that we will be building towards. All projects include a handy README.md that have instructions on how to run them. To get started, we’ll ensure app-complete.js is no longer loaded in index.html. We’ll then have a blank canvas to begin work inside app.js. Open up index.html in your favorite text editor. It should look like this: Project One Your first React Web Application 7

Popular Products

We’ll go over all of the dependencies being loaded under the tag later. The main HTML document is these few lines here:

Popular Products

For this project, we’re using Semantic UI¹⁵ for styling. Semantic UI is a CSS framework, much like Twitter’s Bootstrap¹⁶. It provides us with a grid system and some simple styling. You don’t need to know Semantic UI in order to use this book. We’ll provide all of the styling code that you need. At some point, you might want to check out the docs Semantic UI docs¹⁷ to get familiar with the framework and explore how you can use it in your own projects. The class attributes here are just concerned with style and are safe to ignore. Stripping those away, our core markup is quite succinct: ¹⁵http://semantic-ui.com/ ¹⁶http://getbootstrap.com/ ¹⁷http://semantic-ui.com/introduction/getting-started.html Your first React Web Application 8

Popular Products

We have a title for the page (h1) and a div with an id of content. This div is where we will ultimately mount our React app. We’ll see shortly what that means. The next few lines tell the browser what JavaScript to load. To start building our own application, let’s remove the ./app-complete.js script tag completely. The comments in the code indicate which line to remove: After we save our updated index.html and reload the web browser, we’ll see that our app has disappeared. Our first component Building a React app is all about components. An individual React component can be thought of as a UI component in an app. Let’s take a look at the components in our app: The app’s components We have a hierarchy of one parent component and many sub-components: Your first React Web Application 9 1. ProductList (red): Contains a list of product components 2. Product (orange): Displays a given product Not only do React components map cleanly to UI components, but they are self-contained. The markup, view logic, and often component-specific style is all housed in one place. This feature makes React components reusable. Furthermore, as we’ll see in this chapter and throughout this course, React’s paradigm for component data flow and interactivity is rigidly defined. We’ll see how this well-defined system is beneficial. The classic UI paradigm is to manipulate the DOM with JavaScript directly. As complexity of an app grows, this can lead to all sorts of inconsistencies and headaches around managing state and transitions. In React, when the inputs for a component change, the framework simply re-renders that component. This gives us a robust UI consistency guarantee: With a given set of inputs, the output (how the component looks on the page) will always be the same. Let’s start off by building the ProductList component. We’ll write all of our React code for this section inside the file app.js. Let’s open the app.js file and insert the component: const ProductList = React.createClass({ render: function () { return (
Hello, friend! I am a basic React component.
); }, }); ES6: Prefer const and let over var Both the const and let statements declare variables. They are introduced in ES6. const is a superior declaration in cases where a variable is never re-assigned. Nowhere else in the code will we re-assign the ProductList variable. Using const makes this clear to the reader. It refers to the “constant” state of the variable in the context it is defined within. If the variable will be re-assigned, use let. If you’ve worked with JavaScript before, you’re likely used to seeing variables declared with var: var ProductList = ... We encourage the use of const and let instead of var. In addition to the restriction introduced by Your first React Web Application 10 const, both const and let are block scoped as opposed to function scoped. Typically this separation of scope helps avoid unexpected bugs. React.createClass() To create a component, we use the function React.createClass(). This is how all components are defined in React. We pass in a single argument to this function: a JavaScript object. This class definition object (the argument we pass to the React.createClass() method) in our case has just one key, render, which defines a rendering function. render() is the only required method for a React component. React uses the return value from this method to determine what exactly to render to the page. The createClass() method is one way to create a React component. The other main way of defining one is by using the ES6 classical implementation: class ProductList extends React.Component {} We’ll primarily be using the createClass() method throughout this course. If you have some familiarity with JavaScript, the return value may be surprising: return (
Hello, friend! I am a basic React component.
); The syntax of the return value doesn’t look like traditional JavaScript. We’re using JSX (JavaScript eXtension syntax), a syntax extension for JavaScript written by Facebook. Using JSX enables us to write the markup for our component views in a familiar, HTML-like syntax. In the end, this JSX code compiles to vanilla JavaScript. Although using JSX is not a necessity, we’ll use it in this course as it pairs really well with React. If you don’t have much familiarity with JavaScript, we recommend you follow along and use JSX in your React code too. You’ll learn the boundaries between JSX and JavaScript with experience. Your first React Web Application 11 JSX React components ultimately render HTML which is displayed in the browser. As such, the render() method of a component needs to describe how the view should be represented as HTML. React builds our apps with a fake representation of the Document Object Model (DOM, for short, refers to the browser’s HTML tree that makes up a web page). React calls this the virtual DOM. Without getting deep into details for now, React allows us to describe a component’s HTML representation in JavaScript. JSX was created to make this JavaScript representation of HTML more HTML-like. To understand the difference between HTML and JSX, consider this JavaScript syntax: React.createElement('div', {className: 'ui items'}, 'Hello, friend! I am a basic React component.' ) Which can be represented in JSX as:
Hello, friend! I am a basic React component.
The code readability is slightly improved in the latter example. This is exacerbated in a nested tree structure: React.createElement('div', {className: 'ui items'}, React.createElement('p', null, 'Hello, friend! I am a basic React component.') ) In JSX:

Hello, friend! I am a basic React component.

JSX presents a light abstraction over the JavaScript version, yet the legibility benefits are huge. Readability boosts our app’s longevity and makes it easier to onboard new developers. Even though the JSX above looks exactly like HTML, it’s important to remember that JSX is actually just compiled into JavaScript (ex: React.createElement('div')). During runtime React takes care of rendering the actual HTML in the browser for each component. Your first React Web Application 12 The developer console Our first component is written and we now know that it uses a special flavor of JavaScript called JSX for improved readability. After editing and saving our app.js, let’s refresh the page in our web browser and see what changed: Nothing? Every major browser comes with a toolkit that helps developers working on JavaScript code. A central part of this toolkit is a console. Think of the console as JavaScript’s primary communication medium back to the developer. If JavaScript encounters any errors in its execution, it will alert you in this developer console. To open the console in Chrome, navigate to View > Developer > JavaScript Console. Or, just press Command + Option + J on a Mac or Control + Shift + L on Windows/Linux. Opening the console, we are given a cryptic clue: Uncaught SyntaxError: Unexpected token < Your first React Web Application 13 Error in the console This SyntaxError prevented our code from running. A SyntaxError is thrown when the JavaScript engine encounters tokens or token order that doesn’t conform to the syntax of the language when parsing code. This error type indicates some code is out of place or mistyped. The issue? Our browser’s JavaScript parser exploded when it encountered the JSX. The parser doesn’t know anything about JSX. As far as it is concerned, this < is completely out of place. We know that JSX is an extension to standard JavaScript. Let’s empower our browser’s plain old JavaScript interpreter to use this extension. Babel Babel is a JavaScript transpiler. For those familiar with ES6 JavaScript, Babel turns ES6/ES7 code into ES5 code so that our browser can use lots of these features with browsers that only understand ES5. For our purposes, a handy feature of Babel is that it understands JSX. Babel compiles our JSX into vanilla JS that our browser can then interpret and execute. Let’s tell the browser’s JS engine we want to use babel to compile and run our JavaScript code. The sample code’s index.html already imports Babel in the head tags of index.html: Your first React Web Application 14 All we need to do is tell our JavaScript runtime that our code should be compiled by Babel. We can do this by setting the type attribute when we import the script in index.html from text/javascript to text/babel. Open index.html and change this line: Save index.html and reload the page. Still nothing. However, the console reveals we no longer have any JavaScript compilation errors. Babel successfully compiled our JSX into JS and our browser was able to run that JS without any issues. If your console encounters an error, check that the file has been saved before reloading the browser. If an error still persists, check the code previously added to make sure there aren’t any syntactical mistakes. So what’s happening? We’ve defined the component, but we haven’t yet told React to do anything with it yet. We need to tell the React framework that we want to run our app. ReactDOM.render() We’re going to instruct React to render this ProductList inside a specific DOM node. Add the following code below the component inside app.js: Your first React Web Application 15 ReactDOM.render( , document.getElementById('content') ); We pass in two arguments to the ReactDOM.render() method. The first argument is what we’d like to render. Here, we’re passing in a reference to our React component ProductList in JSX. The second argument is where to render it. We’ll send a reference to the browser’s DOM element. ReactDOM.render([what], [where]); In our code, we have a difference in casing between the different types of React element declarations. We have HTML DOM elements like
and a React component called . In React, native HTML elements always start with a lowercase letter whereas React component names always start with an uppercase letter. With ReactDOM.render() now at the end of app.js, save the file and refresh the page in the browser: We successfully implemented a React component in JSX, ensured it was being compiled to JS, and rendered it in the DOM in the web browser. Our second component Currently, our only component is ProductList. We’ll want ProductList to render a list of products (its “sub-components” or “child components”). In HTML, we could render this entirely in the JSX Your first React Web Application 16 that ProductList returns. Although this works, we don’t get any benefit from encoding our entire app in a single component. Just like we can embed HTML elements in the JSX of our components, we can embed other React components. Let’s build a child component, Product, that will contain a product listing. Just like with the ProductList component, we’ll use the React.createClass() function with a single key of render: const Product = React.createClass({ render: function () { return (
) } }); For every product, we’ll add an image, a title, a description, and an avatar of the post author. The relevant code might look something like: const Product = React.createClass({ render: function () { return (
Fort Knight

Authentic renaissance actors, delivered in just two weeks.

Submitted by:
); }, }); We’ve used a bit of SemanticUI styling in our code here. As we discussed previously, this JSX code will be compiled to JavaScript in the browser. As it runs in the browser as JavaScript, we cannot use Your first React Web Application 17 any reserved JavaScript words in JSX. Setting the class attribute on an HTML element is how we add a Cascading StyleSheet (CSS, for short) to apply styles to the class. In JSX, however, we cannot use class, so React changes the key from class to className. Structurally, the Product component is similar to the ProductList component. Both have a single render() method which returns information about an eventual HTML structure to display. Remember, the JSX components return is not actually the HTML that gets rendered, but is the representation that we want React to render in the DOM. This course looks at this in-depth in later sections. To use the Product component, we can modify our parent ProductList component to list the Product component: const ProductList = React.createClass({ render: function () { return (
); }, }); Save app.js and refresh the web browser. Your first React Web Application 18 With this update, we now have two React components being rendered in our webapp. The ProductList parent component is rendering the Product component as a child nested underneath its root div element. At the moment, the child Product component is static. We hardcoded an image, the name, the description, and author details. To use this component in a meaningful way, we’ll change it to be data-driven and therefore dynamic. Making Product data-driven Attributes in Product like title and description are currently hard-coded. We will need to tweak our Product component to make it data-driven. Having the component be driven by data will allow us to dynamically render the component based upon the data that we give it. Let’s familiarize ourselves with the product data model. The data model In the sample code, we’ve included a file called data.js which contains some example data for our products. In the future, we might fetch this data over a network request (we cover this in later sections). The data.js file contains a JavaScript object called Data that contains an array of JavaScript objects, each representing a product object: id: 1, title: 'Yellow Pail', description: 'On-demand sand castle construction expertise.', url: '#', votes: generateVoteCount(), submitter_avatar_url: 'images/avatars/daniel.jpg', product_image_url: 'images/products/image-aqua.png', }, { Each product has a unique id and a handful of properties including a title and description. votes are randomly generated for each one with the included function generateVoteCount(). We can use the same attribute keys in our React code. Using props We want to modify our Product component so that it no longer uses static, hard-coded attributes, but instead is able to accept data passed down from its parent, ProductList. Setting up our component Your first React Web Application 19 structure in this way enables our ProductList component to dynamically render any number of Product components that each have their own unique attributes. Data flow will look like this: The way data flows from parent to child in React is through props. When a parent renders a child, it can send along props the child can depend upon. Let’s see this in action. First, let’s modify ProductList to pass down props to Product. Using our Data object instead of typing the data in, let’s pluck the first object off of the Data array and use that for the single product: const ProductList = React.createClass({ render: function () { const product = Data[0]; return (
); }, }); Here, the product variable is a JavaScript object that describes the first of our products. We pass the product’s attributes along individually to the Product component using the syntax [prop_- name]=[prop_value]. The syntax of assigning attributes in JSX is exactly the same as HTML and XML. Your first React Web Application 20 There are two interesting things here. The first is the braces ({}) around each of the property values: id={product.id} In JSX, braces are a delimiter, signaling to JSX that what resides in-between the braces is an expression. To pass in a string instead of an expression, for instance, we can do so like this: id='1' Using the ' as a delimiter for the string instead of the {}. JSX attribute values must be delimited by either braces or quotes. If type is important and we want to pass in something like a Number or a null, use braces. Now the ProductList component is passing props down to Product. Our Product component isn’t using them yet, so let’s modify the component to use these props: const Product = React.createClass({ render: function () { return (
{this.props.votes}
Submitted by:
); }, }); In React, we can access all component props through the this.props in a component object. We can access all of the various props we passed along with the names assigned by ProductList. Again, we’re using braces as a delimiter. this is a special keyword in JavaScript. The details about this are a bit nuanced, but for the purposes of the majority of this book, this will be bound to the React component class and we’ll discuss when it differs in later sections. We use this to call methods on the component. For more details on this, check out this page on MDN¹⁸. With our updated app.js file saved, let’s refresh the web browser again: The ProductList component now shows a single product listed, the first object pulled from Data. Now that Product is rendering itself based on the props that it receives from ProductList, our code is poised to render any number of unique products. ¹⁸https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this Your first React Web Application 22 Rendering multiple products Modify the ProductList component again. This time, we’re going to make an array of Product components, each representing an individual object in the Data array. Rather than passing in a single product, let’s map over all of our products: const ProductList = React.createClass({ render: function () { const products = Data.map((product) => { return ( ); }); return (
{products}
); }, }); Before render’s return method, we create a variable called products. We use JavaScript’s map function to iterate over each one of the objects in the Data array. The function passed to map simply returns a Product component. Notably, we’re able to represent the Product component instance in JSX inside of return without issue because it compiles to JavaScript. This Product is created exactly as before with the same props. As such, the products variable ends up just being an array of Product components, which in this case is four total. Your first React Web Application 23 Array’s map method takes a function as an argument. It calls this function with each item inside of the array (in this case, each object inside Data) and builds a new array by using the return value from each function call. Because the Data array has four items, map will call this function four times, once for each item. When map calls this function, it passes in as the first argument an item. The return value from this function call is inserted into the new array that map is constructing. After handling the last item, map returns this new array. Here, we’re storing this new array in the variable products. Note the use of the key={'product-' + product.id} prop. React uses this special property to create unique bindings for each instance of the Product component. The key prop is not used by our Product component, but by the React framework. It’s a special property that we discuss deeper in our advanced components section. For the time being, it’s enough to note that this property needs to be unique per React instance in a map. ES6: Arrow functions Up until this point, we’ve been using the traditional JavaScript function declaration syntax: render: function () { ... } We’ve been using this syntax to declare object methods for our React component classes. Inside of the render() method of ProductList, we pass an anonymous arrow function to map(). Arrow functions were introduced in ES6. Throughout the book, whenever we’re declaring anonymous functions we will use arrow functions. This is for two reasons. Your first React Web Application 24 The first is that the syntax is much terser. Compare this declaration: const timestamps = messages.map(function(m) { return m.timestamp }); To this one: const timestamps = messages.map(m => m.timestamp); Of greater benefit, though, is how arrow functions bind the this object. We cover the semantics of this difference later in the sidebar titled “Arrow functions and this.” After we save the updates to our app.js and refresh the page, we’ll see that we have five total React components at work. We have a single parent component, our ProductList component which contains four child Product components, one for each product object in our Data variable (from data.js). Product components (orange) inside of the ProductList component (red) We added an ‘up vote’ caret icon in our Product component above. If we click on one of these buttons, we’ll see that nothing happens. We’ve yet to hook up an event to the button. Although we have a dynamic React app running in our web browser, this page still lacks interactivity. While React has given us an easy and clean way to organize our HTML thus far and enabled us to drive HTML generation based on a flexible, dynamic JavaScript object, we’ve still yet to tap into its true power in enabling super dynamic, interactive interfaces. The rest of this course digs deep into this power. Let’s start with something simple: the ability to Your first React Web Application 25 up-vote a given product. React the vote (your app’s first interaction) When the up-vote button on each one of the Product components is clicked, we expect it to update the votes attribute for that Product, increasing it by one. In addition, this vote should be updated in the Data variable. While Data is just a JavaScript variable right now, in the future it could just as easily be a remote database. We would need to inform the remote database of the change. While we cover this process in-depth in later sections of this course, we’ll get used to this practice by updating Data. The Product component can’t modify its votes. this.props is immutable. Remember, while the child has access to read its own props, it doesn’t own them. In our app, the parent component, ProductList, owns them. React favors the idea of one-way data flow. This means that data changes come from the “top” of the app and are propagated “downwards” through its various components. A child component does not own its props. Parent components own the props of the child component. We need a way for the Product component to let ProductList know that a click on its up-vote event happened and then let ProductList, the owner of both that product’s props as well as the store (Data), handle the rest. It can update Data and then data will flow downward from Data, through the ProductList component, and finally to the Product component. Propagating the event Fortunately, propagating an event from a child component to a parent is easy. We can pass down functions as props too. We can have the ProductList component give each Product component a function to call when the up-vote button is clicked. Functions passed down through props are the canonical manner in which children communicate events with their parent components. Let’s start by modifying Product to call a function when the up-vote button is clicked. First, add a new component function to Product, handleUpVote(). We’ll anticipate the existence of a new prop called onVote() that’s a function passed down by ProductList: Your first React Web Application 26 const Product = React.createClass({ handleUpVote: function () { this.props.onVote(this.props.id); }, render: function () { // ... We’re setting up an expectation that the ProductList component sends a function as a prop to the Product component. We’ll call this function onVote(). onVote() accepts a single argument. The argument we’re passing in is the id of the product (this.props.id). The id is sufficient information for the parent to deduce which Product component has produced this event. We’ll implement the function shortly. We can have an HTML element inside the Product component call a function when it is clicked. We’ll set the onClick attribute on the a HTML tag that is the up-vote button. By passing the name of the function handleUpVote() to this attribute, it will call our new handleUpVote() function when the up-vote button is clicked: const Product = React.createClass({ handleUpVote: function () { this.props.onVote(this.props.id); }, render: function () { return ( // ...
{this.props.votes}
// ... Let’s define the function in ProductList that we pass down to Product as the prop onVote(). This is the function that the Product component’s handleUpVote() calls whenever the up-vote button is clicked: Your first React Web Application 27 const ProductList = React.createClass({ handleProductUpVote: function (productId) { console.log(productId + " was upvoted."); }, render: function () { const products = Data.map((product) => { return ( ); }); return (
{products}
); }, }); First we define a handleProductUpVote() function on ProductList. We’ve set it up to accept a productId, as anticipated. This function is then passed down as a prop, onVote, just the same as any other prop. ES6: Arrow functions and this Inside ProductList, we use array’s map() method on Data to setup the variable products. We pass an anonymous arrow function to map(). Inside this arrow function, we call this.handleProductUpVote. Here, this is bound to the React object. We introduced arrow functions earlier and mentioned that one of their benefits was how they bind the this object. The traditional JavaScript function declaration syntax (function () {}) will bind this in anonymous functions to the global object. To illustrate the confusion this causes, consider the following Your first React Web Application 28 example: function printSong() { console.log("Oops - The Global Object"); } const jukebox = { songs: [ { title: "Wanna Be Startin' Somethin'", artist: "Michael Jackson", }, { title: "Superstar", artist: "Madonna", }, ], printSong: function (song) { console.log(song.title + " - " + song.artist); }, printSongs: function () { // `this` bound to the object (OK) this.songs.forEach(function (song) { // `this` bound to global object (bad) this.printSong(song); }); }, } jukebox.printSongs(); // > "Oops - The Global Context" // > "Oops - The Global Context" The method printSongs() iterates over this.songs with forEach(). In this context, this is bound to the object (jukebox) as expected. However, the anonymous function passed to forEach() binds its internal this to the global object. As such, this.printSong(song) calls the function declared at the top of the example, not the method on jukebox. JavaScript developers have traditionally used workarounds for this behavior, but arrow functions solve the problem by capturing the this value of the enclosing context. Using an arrow function for printSongs() has the expected result: Your first React Web Application 29 // ... printSongs: function () { this.songs.forEach((song) => { // `this` bound to same `this` as `printSongs()` (`jukebox`) this.printSong(song); }); }, } jukebox.printSongs(); // > "Wanna Be Startin' Somethin' - Michael Jackson" // > "Superstar - Madonna" For this reason, throughout the book we will use arrow functions for all anonymous functions. Saving our updated app.js, refreshing our web browser, and clicking an up-vote will log some text to our JavaScript console: The events are being propagated up to the parent. Finally, we need ProductList to update the store, Data. Your first React Web Application 30 It’s tempting to modify the Data JavaScript object directly. We could hunt through Data until we find the corresponding product and then update its vote count. However, React will have no idea this change occurred. So while the vote count will be updated in the store, this update will not be reflected to the user. This would be equivalent to just making a call to a server to update the vote count but then doing nothing else. The front-end would be none the wiser. In order to move forward, there’s one more critical concept for React components we need to cover: state. Using state Whereas props are immutable and owned by a component’s parent, state is mutable and owned by the component. this.state is private to the component and can be updated with this.setState(). As with props, when the state updates the component will re-render itself. Every React component is rendered as a function of its this.props and this.state. This rendering is deterministic. This means that given a set of props and a set of state, a React component will always render a single way. As we mentioned earlier, this approach makes for a powerful UI consistency guarantee. As we are mutating the data for our products (the number of votes), we should consider this data to be stateful. We should treat Data as some external store, which is used to update this.state for ProductList. Let’s modify the ProductList component’s render function so that it uses state as opposed to accessing Data directly. In order to tell React that our component isstateful, we’ll define the function getInitialState() and return a non-falsey value: const ProductList = React.createClass({ getInitialState: function () { return { products: [], }; }, handleProductUpVote: function (productId) { console.log(productId + " was upvoted."); }, render: function () { const products = this.state.products.map((product) => { return ( { return b.votes - a.votes; }); this.setState({ products: products }); }, handleProductUpVote: function (productId) { console.log(productId + " was upvoted."); }, render: function () { // ... In our ProductList component, the componentDidMount() function uses the native JavaScript Array’s sort() method to ensure that we sort products based upon the number of votes in descending order. This sorted array of products is then used in the special component method this.setState(). As anticipated, this method updates this.state and re-renders the component. As we’ll need to update and sort the state after we click on an up-vote button, we can define this functionality as a single function so we don’t need to duplicate this functionality. We’ll call this function updateState(): Your first React Web Application 32 const ProductList = React.createClass({ getInitialState: function () { return { products: [], }; }, componentDidMount: function () { this.updateState(); }, updateState: function () { const products = Data.sort((a, b) => { return b.votes - a.votes; }); this.setState({ products: products }); }, handleProductUpVote: function (productId) { console.log(productId + " was upvoted."); }, render: function () { // ... Never modify state outside of this.setState(). This function has important hooks around state modification that we would be bypassing. We discuss state management in detail throughout the book. Array’s sort() method takes an optional function as an argument. If the function is omitted, it will just sort the array by each item’s Unicode code point value. This is rarely what a programmer desires. If the function is supplied, elements are sorted according to the functions return value. On each iteration, the arguments a and b are two elements in the array. Sorting depends on the return value of the function: 1. If the return value is less than 0, a should come first (have a lower index). 2. If the return value is greater than 0, b should come first. 3. If the return value is equal to 0, leave order of a and b unchanged with respect to each other. sort() mutates the original array it was called on. Later on in the course, we discuss why mutating arrays or objects can be a dangerous pattern. Your first React Web Application 33 If we save and refresh now, we see that the products are back. Updating state With state management in place, we need to modify handleProductUpVote() inside ProductList. When handleProductUpVote() is invoked from inside the Product component, it should update Data and then trigger a state update for ProductList: const ProductList = React.createClass({ // ... handleProductUpVote: function (productId) { Data.forEach((el) => { if (el.id === productId) { el.votes = el.votes + 1; return; } }); this.updateState(); }, // ... We use Array’s forEach() to traverse Data. When a matching object is found (based upon the object’s id), its votes attribute is incremented by 1. After Data is modified, we’ll call this.updateState() to update the state to the current products value. The component’s state is then updated and React intelligently re-renders the UI to reflect these updates. Array’s forEach() method executes the provided function once for each element present in the array in ascending order. Handling these updates on a remote store would be a fairly trivial update. Instead of modifying Data inside of handleProductUpVote(), we would make a call to a remote service. The server’s response would trigger a callback that would then make a subsequent call to the service asking for the most recent data, updating the state with the data from the response. We’ll be exploring server communication in this course’s next project. Save app.js, refresh the browser, and cross your fingers: Your first React Web Application 34 At last, the vote counters are working! Try up-voting a product a bunch of times and notice how it quickly jumps above products with lower vote counts. Technically, we can perform the same operation in this.updateState() within getInitialState() as well, bypassing the brief period where the state is empty. However, this is usually bad practice for a variety of reasons. We look into why in our advanced components section, but briefly, our state will be set by a call to a remote server. As React is still performing the initial render of the app, we’d have to halt it in its tracks, block on a web request, and then update the state when the result is returned. Instead, by just giving React a valid blank “scaffold” for the state, React will render everything first then make parallel, asynchronous calls to whichever servers to fill in the details. Much more efficient. Again, the style of this app differs slightly from the demo we saw at the beginning of this section. If you’d like to add some additional style to your components, refer to the HTML structure in app-complete.js. Congratulations! We have just completed our first React app. Not so bad, eh? Your first React Web Application 35 There are a ton of powerful features we’ve yet to go over, yet all of them build upon the core fundamentals we just covered: 1. Think about and organize your app into components 2. JSX and the render method 3. Data flow from parent to children through props 4. Event flow from children to parent through functions 5. State vs props 6. How to manipulate state 7. Utilizing React lifecycle methods Onward! Chapter Exercises 1. Add down-voting capability to each Product. You can insert a down arrow with this JSX snippet: 2. Add a “sort direction” button to the top of ProductList, above all the products. It should enable the user to toggle sorting products by ascending or descending. Components A time-logging app In the last chapter, we described how React organizes apps into components and how data flows between parent and child components. And we discussed core concepts such as how we manage state and pass data between components using props. In this chapter, we construct a more intricate application. We investigate a pattern that we will use to build all React apps and then put those steps to work to build an interface for managing timers. In this time-tracking app, a user can add, delete, and modify various timers. Each timer corresponds to a different task that the user would like to keep time for: This app will have significantly more interactive capabilities than the one built in the last chapter. This will present us with some interesting challenges that will deepen our familiarity with React’s core concepts. Components 37 Getting started Sample Code As with all chapters, before beginning make sure you’ve downloaded the book’s sample code and have it at the ready. Previewing the app Let’s begin by playing around with a completed implementation of the app. In your terminal, cd into the time_tracking_app directory: $ cd time_tracking_app Use npm to install all the dependencies: $ npm install Then boot the server: $ npm run server Now you can view the app in your browser. Open your browser and enter the URL http://localhost:3000¹⁹. Play around with it for a few minutes to get a feel for all the functionality. Refresh and note that your changes have been persisted. Prepare the app In your terminal, run ls -1p to see the project’s layout: $ ls -1p ¹⁹http://localhost:3000 Components 38 README.md data.json node_modules/ package.json public/ server.js There are a few organizational changes from the last project. First, notice that there is now a server.js in this project. In the last chapter, we used a pre-built Node package (called http-server) to serve our assets. This time we have a custom-built server which serves our assets and also adds a persistence layer. We will cover the server in detail in the next chapter. When you visit a website, assets are the files that your browser downloads and uses to display the page. index.html is delivered to the browser and inside its head tags it specifies which additional files from the server the browser needs to download. In the last project, our assets were index.html as well as our stylesheets and images. In this project, everything under public/ is an asset. In the voting app, we loaded all of our app’s initial data from a JavaScript variable, loaded in the file data.js. This time, we’re going to eventually store it in the text file data.json. This brings the behavior a bit closer to a database. By using a JSON file, we can make edits to our data that will be persisted even if the app is closed. JSON stands for JavaScript Object Notation. JSON enables us to serialize a JavaScript object and read/write it from a text file. If you’re not familiar with JSON, take a look at data.json. Pretty recognizeable, right? JavaScript has a built-in mechanism to parse the contents of this file and initialize a JavaScript object with its data. Peek inside public: $ cd public $ ls -1p And you will find some familiar files: Components 39 vendor/ app-complete.js app.js client.js helpers.js index.html style.css Again, we’ll be editing only the files index.html and app.js. index.html is the actual webpage which loads our React app which is written inside of app.js. helpers.js and client.js contain some pre-written JavaScript functions that we will use in our app. As before, our first step is to ensure app-complete.js is no longer loaded in index.html. We’ll instead load the empty file app.js. Open up index.html. It looks like this: Project Two: Timers

Timers

Components 40 Overall, this file is very similar to the one we used in our voting app. We load in our dependencies within the head tags (the assets). Inside of body we have a few elements. This div is where we will ultimately mount our React app:
And this script tag is where we instruct the browser to load app.js into the page: Do as the comment says and delete the line that loads app-complete.js: Save index.html. If you reload the page now, you’ll see the app has disappeared. Breaking the app into components As we did with our last project, we begin by breaking our app down into its components. Again, visual components often map tightly to their respective React components. Let’s examine the interface of our app: Components 41 In the last project, we had ProductList and Product components. The first contained instances of the second. Here, we spot the same pattern, this time with TimerList and Timer components: Components 42 However, there’s one subtle difference: This list of timers has a little “+” icon at the bottom. As we saw, we’re able to add new timers to the list using this button. So, in reality, the TimerList component isn’t just a list of timers. It also contains a widget to create new timers. Think about components as you would functions or objects. The single responsibility principle²⁰ applies. A component should, ideally, only be responsible for one piece of functionality. So, the proper response here is for us to shrink TimerList back into its responsibility of just listing timers and to nest it under a parent component. We’ll call the parent TimersDashboard. TimersDashboard will have TimerList and the “+”/create form widget as children: ²⁰https://en.wikipedia.org/wiki/Single_responsibility_principle Components 43 Not only does this separation of responsibilities keep components simple, but it often also improves their re-usability. In the future, we can now drop the TimerList component anywhere in the app where we just want to display a list of timers. This component no longer carries the responsibility of also creating timers, which might be a behavior we want to have for just this dashboard view. How you name your components is indeed up to you, but having some consistent rules around language as we do here will greatly improve code clarity. In this case, developers can quickly reason that any component they come across that ends in the word List simply renders a list of children and no more. The “+”/create form widget is interesting because it has two distinct representations. When the “+” button is clicked, the widget transmutes into a form. When the form is closed, the widget transmutes back into a “+” button. There are two approaches we could take. The first one is to have the parent component, TimersDashboard, decide whether or not to render a “+” component or a form component based on some piece of stateful data. It could swap between the two children. However, this adds more responsibility to TimersDashboard. The alternative is to have a new child component own the single responsibility of determining whether or not to display a “+” button or a create timer form. We’ll call it ToggleableTimerForm. As a child, it can either render the component TimerForm or the HTML markup for the “+” button. At this point, we’ve carved out four components: Components 44 Now that we have a sharp eye for identifying overburdened components, another candidate should catch our eye: A single timer: Displaying time (left) vs. edit form (right) The timer itself has a fair bit of functionality. It can transform into an edit form, delete itself, and start and stop itself. Do we need to break this up? And if so, how? Displaying a timer and editing a timer are indeed two distinct UI elements. They should be two distinct React components. Like ToggleableTimerForm, we need some container component that renders either the timer’s face or its edit form depending on if the timer is being edited. Components 45 We’ll call this EditableTimer. The child of EditableTimer will then be either a Timer component or the edit form component. The form for creating and editing timers is very similar, so let’s assume that we can use the component TimerForm in both contexts: As for the other functionality of the timer, like the start and stop buttons, it’s a bit tough to determine at this point whether or not they should be their own components. We can trust that the answers will be more apparent after we’ve written some code. Working back up the component tree, we can see that the name TimerList would be a misnomer. It really is a EditableTimerList. Everything else looks good. So, we have our final component hierarchy, with some ambiguity around the final state of the timer component: Components 46 • TimersDashboard (green): Parent container – EditableTimerList (red): Displays a list of timer containers * EditableTimer (aqua): Displays either a timer or a timer’s edit form · Timer (yellow): Displays a given timer · TimerForm (blue): Displays a given timer’s edit form – ToggleableTimerForm (purple): Displays a form to create a new timer * TimerForm (not displayed): Displays a new timer’s create form Represented as a hierarchical tree: Components 47 In our previous app, ProductList handled not only rendering components, but also the responsibility of handling up-votes and talking to the store. While this worked for that app, you can imagine that as a codebase expands, there may come a day where we’d want to free ProductList of this responsibility. For example, imagine if we added a “sort by votes” feature to ProductList. What if we wanted some pages to be sortable (category pages) but other pages to be static (top 10)? We’d want to “hoist” sort responsibility up to a parent component and make ProductList the straightforward list renderer that it should be. This new parent component could then include the sorting-widget component and then pass down the ordered products to the ProductList component. The steps for building React apps Now that we have a good understanding of the composition of our components, we’re ready to build a static version of our app. Ultimately, our top-level component will communicate with a server. The Components 48 server will be the initial source of state, and React will render itself according to the data the server provides. Our app will also send updates to the server, like when a timer is started: However, it will simplify things for us if we start off with static components, as we did in the last chapter. Our React components will do little more than render HTML. Clicking on buttons won’t yield any behavior as we will not have wired up any interactivity. This will enable us to lay the framework for the app, getting a clear idea of how the component tree is organized. Next, we can determine what the state should be for the app and in which component it should live. We’ll start off by just hard-coding the state into the components instead of loading it from the server. At that point, we’ll have the data flow from parent to child in place. Then we can add inverse data flow, propagating events from child to parent. Finally, we’ll modify the top-level component to have it communicate with the server. In fact, the development of each React app will follow this pattern: 1. Break the app into components 2. Build a static version of the app 3. Determine what should be stateful 4. Determine in which component each piece of state should live 5. Hard-code initial states 6. Add inverse data flow Components 49 7. Add server communication We actually followed this pattern in the last project: 1. Break the app into components We looked at the desired UI and determined we wanted ProductList and Product components. 2. Build a static version of the app Our components started off without using state. Instead, we had ProductList pass down static props to Product. 3. Determine what should be stateful In order for our application to become interactive, we had to be able to modify the vote property on each product. Each product had to be mutable and therefore stateful. 4. Determine in which component each piece of state should live ProductList managed the voting state using React component class methods. 5. Hard-code initial state When we re-wrote ProductList to use this.state, we had it use the hard-coded variable Data. 6. Add inverse data flow We defined the handleUpVote function in ProductList and passed it down in props so that each Product could inform ProductList of up-vote events. 7. Add server communication We did not add a server component to our last app, but we will be doing so in this one. If steps in this process aren’t completely clear right now, don’t worry. The purpose of this chapter is to familiarize yourself with this procedure. We’ve already covered step (1) and have a good understanding of all of our components, save for some uncertainty down at the Timer component. Step (2) is to build a static version of the app. As in the last project, this amounts to defining React components, their hierarchy, and their HTML representation. We completely avoid state for now. Step 2: Build a static version of the app TimersDashboard Let’s start off with the TimersDashboard component. Again, all of our React code for this chapter will be inside of the file public/app.js. We’ll begin by defining a familiar function, render(): Components 50 const TimersDashboard = React.createClass({ render: function () { return (
); }, }); This component renders its two child components nested under div tags. TimersDashboard passes down one prop to ToggleableTimerForm: isOpen. This is used by the child component to determine whether to render a “+” or TimerForm. When ToggleableTimerForm is “open” the form is being displayed. As in the last chapter, don’t worry about the className attribute on the div tags. This will ultimately define the class on HTML div elements and is purely for styling purposes. In this example, classes like ui three column centered grid all come from the CSS framework Semantic UI²¹. The framework is included in the head of index.html. We will define EditableTimerList next. We’ll have it render two EditableTimer components. One will end up rendering a timer’s face. The other will render a timer’s edit form: const EditableTimerList = React.createClass({ render: function () { return (
); }, }); We’re passing five props to each child component. The key difference between the two EditableTimer components is the value being set for editFormOpen. We’ll use this boolean to instruct EditableTimer which sub-component to render. The purpose of the prop runningSince will be covered later on in the app’s development. EditableTimer EditableTimer returns either a TimerForm or a Timer based on the prop editFormOpen: const EditableTimer = React.createClass({ render: function () { if (this.props.editFormOpen) { return ( ); } else { return ( ); Components 52 } }, }); Note that title and project are passed down as props to TimerForm. This will enable the component to fill in these fields with the timer’s current values. TimerForm We’ll build an HTML form that will have two input fields. The first input field is for the title and the second is for the project. It also has a pair of buttons at the bottom: const TimerForm = React.createClass({ render: function () { const submitText = this.props.title ? 'Update' : 'Create'; return (
); }, }); Components 53 Look at the input tags. We’re specifying that they have type of text and then we are using the React property defaultValue. When the form is used for editing as it is here, this sets the fields to the current values of the timer as desired. Later, we’ll use TimerForm again within ToggleableTimerForm for creating timers. ToggleableTimerForm will not pass TimerForm any props. this.props.title and this.props.project will therefore return undefined and the fields will be left empty. At the beginning of render(), before the return statement, we define a variable submitText. This variable uses the presence of this.props.title to determine what text the submit button at the bottom of the form should display. If title is present, we know we’re editing an existing timer, so it displays “Update.” Otherwise, it displays “Create.” With all of this logic in place, TimerForm is prepared to render a form for creating a new timer or editing an existing one. We used an expression with the ternary operator to set the value of submitText. The syntax is: 1 condition ? expression1 : expression2 If the condition is true, the operator returns the value of expression1. Otherwise, it returns the value of expression2. In our example, the variable submitText is set to the returned expression. ToggleableTimerForm Let’s turn our attention next to ToggleableTimerForm. Recall that this is a wrapper component around TimerForm. It will display either a “+” or a TimerForm. Right now, it accepts a single prop, isOpen, from its parent that instructs its behavior: const ToggleableTimerForm = React.createClass({ render: function () { if (this.props.isOpen) { return ( ); } else { return (
); } }, }); As noted earlier, TimerForm does not receive any props from ToggleableTimerForm. As such, its title and project fields will be rendered empty. The return statement under the else block is the markup to render a “+” button. You could make a case that this should be its own React component (say PlusButton) but at present we’ll keep the code inside ToggleableTimerForm. Timer Time for the Timer component. Again, don’t worry about all the div and span elements and className attributes. We’ve provided these for styling purposes: const Timer = React.createClass({ render: function () { const elapsedString = helpers.renderElapsedString(this.props.elapsed); return (
{this.props.title}
{this.props.project}

{elapsedString}

Components 55
Start
); }, }); elapsed in this app is in milliseconds. This is the representation of the data that React will keep. This is a good representation for machines, but we want to show our carbon-based users a more readable format. We use a function defined in helpers.js, renderElapsedString(). You can pop open that file if you’re curious about how it’s implemented. The string it renders is in the format ‘HH:MM:SS’. Note that we could store elapsed in seconds as opposed to milliseconds, but JavaScript’s time functionality is all in milliseconds. We keep elapsed consistent with this for simplicity. As a bonus, our timers are also slightly more accurate, even though they round to seconds when displayed to the user. Render the app With all of the components defined, the last step before we can view our static app is to ensure we call ReactDOM#render(). Put this at the bottom of the file: ReactDOM.render( , document.getElementById('content') ); Again, we specify with ReactDOM#render() which React component we want to render and where in our HTML document (index.html) to render it. In this case, we’re rendering TimersDashboard at the div with the id of content. Try it out Save app.js and boot the server (npm run server). Find it at localhost:3000²²: ²²localhost:3000 Components 56 Tweak some of the props and refresh to see the results. For example: • Flip the prop passed down to ToggleableTimerForm from true to false and see the “+” button render. • Flip parameters on editFormOpen and witness EditableTimer flip the child it renders accordingly. Let’s review all of the components represented on the page: Inside TimersDashboard are two child components: EditableTimerList and ToggleableTimerForm. EditableTimerList contains two EditableTimer components. The first of these has a Timer component as a child and the second a TimerForm. These bottom-level components — also known as leaf components — hold the majority of the page’s HTML. This is generally the case. The components above leaf components are primarily concerned with orchestration. ToggleableTimerForm renders a TimerForm. Notice how the two forms on the page have different language for their buttons, as the first is updating and the second is creating. Components 57 Step 3: Determine what should be stateful In order to bestow our app with interactivity, we must evolve it from its static existence to a mutable one. The first step is determining what, exactly, should be mutable. Let’s start by collecting all of the data that’s employed by each component in our static app. In our static app, data will be wherever we are defining or using props. We will then determine which of that data should be stateful. TimersDashboard In our static app, this declares two child components. It sets one prop, which is the isOpen boolean that is passed down to ToggleableTimerForm. EditableTimerList This declares two child components, each which have props corresponding to a given timer’s properties. EditableTimer This uses the prop editFormOpen. Timer This uses all the props for a timer. TimerForm This uses the title and project props when editing an existing timer. State criteria We can apply criteria to determine if data should be stateful: These questions are from the excellent article by Facebook called “Thinking In React”. You can read the original article here²³. 1. Is it passed in from a parent via props? If so, it probably isn’t state. A lot of the data used in our child components are already listed in their parents. This criterion helps us de-duplicate. For example, “timer properties” is listed multiple times. When we see the properties declared in EditableTimerList, we can consider it state. But when we see it elsewhere, it’s not. 2. Does it change over time? If not, it probably isn’t state. This is a key criterion of stateful data: it changes. ²³https://facebook.github.io/react/docs/thinking-in-react.html Components 58 3. Can you compute it based on any other state or props in your component? If so, it’s not state. For simplicity, we want to strive to represent state with as few data points as possible. Applying the criteria TimersDashboard • isOpen boolean for ToggleableTimerForm Stateful. The data is defined here. It changes over time. And it cannot be computed from other state or props. EditableTimerList • Timer properties Stateful. The data is defined in this component, changes over time, and cannot be computed from other state or props. EditableTimer • editFormOpen for a given timer Stateful. The data is defined in this component, changes over time, and cannot be computed from other state or props. Timer • Timer properties In this context, not stateful. Properties are passed down from the parent. TimerForm • title and project properties for a given timer Not stateful. Properties are passed down from parent. We’ve identified our stateful data: • The list of timers and properties of each timer • Whether or not the edit form of a timer is open • Whether or not the create form is open Components 59 Step 4: Determine in which component each piece of state should live While the data we’ve determined to be stateful might live in certain components in our static app, this does not indicate the best position for it in our stateful app. Our next task is to determine the optimal place for each of our three discrete pieces of state to live. This can be challenging at times but, again, we can apply the following steps from Facebook’s guide “Thinking in React²⁴” to help us with the process: For each piece of state: • Identify every component that renders something based on that state. • Find a common owner component (a single component above all the components that need the state in the hierarchy). • Either the common owner or another component higher up in the hierarchy should own the state. • If you can’t find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component. Let’s apply this method to our application: The list of timers and properties of each timer At first glance, we may be tempted to conclude that TimersDashboard does not appear to use this state. Instead, the first component that uses it is EditableTimerList. This matches the location of the declaration of this data in our static app. Because ToggleableTimerForm doesn’t appear to use the state either, we might deduce that EditableTimerList must then be the common owner. While this may be the case for displaying timers, modifying them, and deleting them, what about creates? ToggleableTimerForm does not need the state to render, but it can affect state. It needs to be able to insert a new timer. It will propagate the data for the new timer up to TimersDashboard. Therefore, TimersDashboard is truly the common owner. It will render EditableTimerList by passing down the timer state. It can the handle modifications from EditableTimerList and creates from ToggleableTimerForm, mutating the state. The new state will flow downward through EditableTimerList. ²⁴https://facebook.github.io/react/docs/thinking-in-react.html Components 60 Whether or not the edit form of a timer is open In our static app, EditableTimerList specifies whether or not a EditableTimer should be rendered with its edit form open. Technically, though, this state could just live in each individual EditableTimer. No parent component in the hierarchy depends on this data. Storing the state in EditableTimer will be fine for our current needs. But there are a few requirements that might require us to “hoist” this state up higher in the component hierarchy in the future. For instance, what if we wanted to impose a restriction such that only one edit form could be open at a time? Then it would make sense for EditableTimerList to own the state, as it would need to inspect it to determine whether to allow a new “edit form open” event to succeed. If we wanted to allow only one form open at all, including the create form, then we’d hoist the state up to TimersDashboard. Visibility of the create form TimersDashboard doesn’t appear to care about whether ToggleableTimerForm is open or closed. It feels safe to reason that the state can just live inside ToggleableTimerForm itself. So, in summary, we’ll have three pieces of state each in three different components: • Timer data will be owned and managed by TimersDashboard. • Each EditableTimer will manage the state of its timer edit form. • The ToggleableTimerForm will manage the state of its form visibility. Step 5: Hard-code initial states We’re now well prepared to make our app stateful. At this stage, we won’t yet communicate with the server. Instead, we’ll define our initial states within the components themselves. This means hard-coding a list of timers in the top-level component, TimersDashboard. For our two other pieces of state, we’ll have the components’ forms closed by default. After we’ve added initial state to a parent component, we’ll make sure our props are properly established in its children. Adding state to TimersDashboard Start by modifying TimersDashboard to hold the timer data directly inside the component: Components 61 const TimersDashboard = React.createClass({ getInitialState: function () { return { timers: [ { title: 'Practice squat', project: 'Gym Chores', id: uuid.v4(), elapsed: 5456099, runningSince: Date.now(), }, { title: 'Bake squash', project: 'Kitchen Chores', id: uuid.v4(), elapsed: 1273998, runningSince: null, }, ], }; }, render: function () { return (
); }, }); In getInitialState, we set the state to an object with the key timers. timers points to an array with two timer objects. For the id property, we’re using a library called uuid. We load this library in index.html. We use uuid.v4() to randomly generate a Universally Unique IDentifier²⁵ for each item. ²⁵https://en.wikipedia.org/wiki/Universally_unique_identifier Components 62 A UUID is a string that looks like this: 2030efbd-a32f-4fcc-8637-7c410896b3e3 Receiving props in EditableTimerList EditableTimerList receives the list of timers as a prop, timers. Modify that component to use those props: const EditableTimerList = React.createClass({ render: function () { const timers = this.props.timers.map((timer) => ( )); return (
{timers}
); }, }); Hopefully this looks familiar. We’re using map on the timers array to build a list of EditableTimer components. This is exactly how we built our list of Product components inside ProductList in the last chapter. We pass the id down to EditableTimer as well. This is a bit of eager preparation. Remember how Product communicated up to ProductList by calling a function and passing in its id? It’s safe to assume we’ll be doing this again. Props vs. state With your renewed understanding of React’s state paradigm, let’s reflect on props again. Components 63 Remember, props are state’s immutable accomplice. What existed as mutable state in TimersDashboard is passed down as immutable props to EditableTimerList. We talked at length about what qualifies as state and where state should live. Mercifully, we do not need to have an equally lengthy discussion about props. Once you understand state, you can see how props act as its one-way data pipeline. State is managed in some select parent components and then that data flows down through children as props. If state is updated, the component managing that state re-renders by calling render(). This, in turn, causes any of its children to re-render as well. And the children of those children. And on and on down the chain. Let’s continue our own march down the chain. Adding state to EditableTimer In the static version of our app, EditableTimer relied on editFormOpen as a prop to be passed down from the parent. We decided that this state could actually live here in the component itself. Again, we use getInitialState to setup state for the component. We set the initial value of editFormOpen to false, which means that the form starts off as closed. We’ll also pass the id property down the chain: const EditableTimer = React.createClass({ getInitialState: function () { return { editFormOpen: false, }; }, render: function () { if (this.state.editFormOpen) { return ( ); } else { return ( ); } }, }); Timer and TimerForm remain stateless If you look at Timer and TimerForm, you’ll see that they do not need to be modified. They’ve always been using exclusively props and are so far unaffected by our refactor. Adding state to ToggleableTimerForm We know that we’ll need to tweak ToggleableTimerForm as we’ve assigned it some stateful responsibility. While we’re here, we can also add a little bit of interactivity: const ToggleableTimerForm = React.createClass({ getInitialState: function () { return { isOpen: false, }; }, handleFormOpen: function () { this.setState({ isOpen: true }); }, render: function () { if (this.state.isOpen) { return ( ); } else { return (
Components 65 ); } }, }); However small, our app’s first bit of interactivity in place. Like the up-vote button in the last app, we use the onClick property on button to invoke a function, handleFormOpen(). handleFormOpen() modifies the state, setting isOpen to true. This causes the component to re-render. When render() is called this second time around, this.state.isOpen is true and ToggleableTimerForm renders TimerForm. Neat. We’ve established our stateful data inside our elected components. Our downward data pipeline, props, is assembled. We’re ready — and perhaps a bit eager — to build out interactivity using inverse data flow. But before we do, let’s save and reload the app to ensure everything is working. We expect to see new example timers based on the hard-coded data in TimersDashboard. We also expect clicking the “+” button toggles open a form: Step 6: Add inverse data flow As we saw in the last chapter, children communicate with parents by calling functions that are handed to them via props. In the ProductHunt app, when an up-vote was clicked Product didn’t do any data management. It was not the owner of its state. Instead, it called a function given to it by ProductList, passing in its id. ProductList was then able to manage state accordingly. We are going to need inverse data flow in two areas: Components 66 • TimerForm needs to propagate create and update events (create while under ToggleableTimerForm and update while under EditableTimer). Both events will eventually reach TimersDashboard. • Timer has a fair amount of behavior. It needs to handle delete and edit clicks, as well as the start and stop timer logic. Let’s start with TimerForm. TimerForm To get a clear idea of what exactly TimerForm will require, we’ll start by adding event handlers to it and then work our way backwards up the hierarchy. TimerForm needs two event handlers: • When the form is submitted (creating or updating a timer) • When the “Cancel” button is clicked (closing the form) TimerForm will receive two functions as props to handle each event. The parent component that uses TimerForm is responsible for providing these functions: • props.onFormSubmit(): called when the form is submitted • props.onFormClose(): called when the “Cancel” button is clicked As we’ll see soon, this empowers the parent component to dictate what the behavior should be when these events occur. Implement these event handlers now: const TimerForm = React.createClass({ handleSubmit: function () { this.props.onFormSubmit({ id: this.props.id, title: this.refs.title.value, project: this.refs.project.value, }); }, render: function () { const submitText = this.props.id ? 'Update' : 'Create'; return (
Components 67
); }, }); Starting with the form itself, notice how we again capture click events using onClick, an attribute on both button elements. We also made one modification to both input elements. We added the attributes ref='title' and ref='project'. Looking at handleSubmit(), you can see how we use this.refs to access the values of these fields. handleSubmit() calls a yet-to-be-defined function, onFormSubmit(). It passes in a data object with id, title, and project attributes, extracting values from this.refs for title and project. id is pulled from this.props. This means id will be undefined for creates, as no id exists yet. For the cancel button, we just pass in the prop-function onFormClose() directly. We do not need to Components 68 supply this function with any arguments so there’s no need to define an additional handler function as we did with submits. Note that we also tweaked submitText so that is uses this.props.id to determine if the submission will be a create or update. Using the id property to determine whether or not an object has been created is a more common practice. ToggleableTimerForm Let’s chase this event as it bubbles up the component hierarchy. First, we’ll modify ToggleableTimerForm. We need it to pass down two prop-functions to TimerForm, onFormClose() and onFormSubmit(): // Inside ToggleableTimerForm handleFormOpen: function () { this.setState({ isOpen: true }); }, handleFormClose: function () { this.setState({ isOpen: false }); }, handleFormSubmit: function (timer) { this.props.onFormSubmit(timer); this.setState({ isOpen: false }); }, render: function () { if (this.state.isOpen) { return ( ); } else { Looking first at the render() function, we can see we pass in the two functions as props. Functions are just like any other prop. Of most interest here is handleFormSubmit(). Remember, ToggleableTimerForm is not the manager of timer state. TimerForm has an event it’s emitting, in this case the submission of a new timer. ToggleableTimerForm is just a proxy of this message. So, when the form is submitted, it calls its own prop-function props.onFormSubmit(). We’ll eventually define this function in TimersDashboard. Components 69 handleFormSubmit() accepts the argument timer. Recall that in TimerForm this argument is an object containing the desired timer properties. We just pass that argument along here. After invoking onFormSubmit(), handleFormSubmit() calls setState() to close its form. Note that the result of onFormSubmit() will not impact whether or not the form is closed. We invoke onFormSubmit(), which may eventually create an asynchronous call to a server. Execution will continue before we hear back from the server which means setState() will be called. If onFormSubmit() fails — such as if the server is temporarily unreachable — we’d ideally have some way to display an error message and re-open the form. We leave the implementation of this improvement as an exercise at the end of the chapter. TimersDashboard We’ve reached the top of the hierarchy, TimersDashboard. As this component will be responsible for the data for the timers, it is here that we will define the logic for handling the events we’re capturing down at the leaf components. The first event we’re concerned with is the submission of a form. When this happens, either a new timer is being created or an existing one is being updated. We’ll use two separate functions to handle the two distinct events: • handleCreateFormSubmit() will handle creates and will be the function passed to ToggleableTimerForm • handleEditFormSubmit() will handle updates and will be the function passed to EditableTimerList Both functions travel down their respective component hierarchies until they reach TimerForm as the prop onFormSubmit(). Let’s start with handleCreateFormSubmit, which inserts a new timer into our timer list state: Components 70 // Inside TimersDashboard handleCreateFormSubmit: function (timer) { this.createTimer(timer); }, createTimer: function (timer) { const t = helpers.newTimer(timer); this.setState({ timers: this.state.timers.concat(t), }); }, render: function () { return (
); }, We create the timer object with helpers.newTimer(). You can peek at the implementation inside of helpers.js. We pass in the object that originated down in TimerForm. This object has title and project properties. helpers.newTimer() returns an object with those title and project properties as well as a generated id. The next line calls setState(), appending the new timer to our array of timers held under timers. We pass the whole state object to setState(). concat() creates a new array that contains the elements of the array it was called on followed by the elements passed in as arguments. If an array is passed in as an argument, its elements are appended to the new array. For example: > [ 1, 2, 3 ].concat([ 4, 5 ]); => [ 1, 2, 3, 4, 5 ] Components 71 You might wonder: why separate handleCreateFormSubmit() and createTimer()? While not strictly required, the idea here is that we have one function for handling the event (handleCreateFormSubmit()) and another for performing the operation of creating a timer (createTimer()). This separation follows from the Single Responsibility Principle and enables us to call createTimer() from elsewhere if needed. We’ve finished wiring up the create timer flow from the form down in TimerForm up to the state managed in TimersDashboard. Save app.js and reload your browser. Toggle open the create form and create some new timers: Updating timers We need to give the same treatment to the update timer flow. However, as you can see in the current state of the app, we haven’t yet added the ability for a timer to be edited. So we don’t have a way to display an edit form, which will be a prerequisite to submitting one. To display an edit form, the user clicks on the edit icon on a Timer. This should propagate an event up to EditableTimer and tell it to flip its child component, opening the form. Components 72 Adding editability to Timer To notify our app that the user wants to edit a timer we need to add an onClick attribute to the span tag of the edit button. We anticipate a prop-function, onEditClick(): {/* Inside Timer.render() */}
Updating EditableTimer Now we’re prepared to update EditableTimer. Again, it will display either the TimerForm (if we’re editing) or an individual Timer (if we’re not editing). Let’s add event handlers for both possible child components. For TimerForm, we want to handle the form being closed or submitted. For Timer, we want to handle the edit icon being pressed: // Inside EditableTimer handleEditClick: function () { this.openForm(); }, handleFormClose: function () { this.closeForm(); }, handleSubmit: function (timer) { this.props.onFormSubmit(timer); this.closeForm(); }, closeForm: function () { this.setState({ editFormOpen: false }); }, openForm: function () { this.setState({ editFormOpen: true }); }, render: function () { Components 73 We pass these event handlers down as props: render: function () { if (this.state.editFormOpen) { return ( ); } else { return ( ); } }, Look a bit familiar? EditableTimer handles the same events emitted from TimerForm in a very similar manner as ToggleableTimerForm. This makes sense. Both EditableTimer and ToggleableTimerForm are just intermediaries between TimerForm and TimersDashboard. TimersDashboard is the one that defines the submit function handlers and assigns them to a given component tree. Like ToggleableTimerForm, EditableTimer doesn’t do anything with the incoming timer. In handleSubmit(), it just blindly passes this object along to its prop-function onFormSubmit(). It then closes the form with closeForm(). We pass along a new prop to Timer, onEditClick. The behavior for this function is defined in handleEditClick, which modifies the state for EditableTimer, opening the form. Updating EditableTimerList Moving up a level, we make a one-line addition to EditableTimerList to send the submit function from TimersDashboard to each EditableTimer: Components 74 // Inside EditableTimerList const timers = this.props.timers.map((timer) => ( )); // ... EditableTimerList doesn’t need to do anything with this event so again we just pass the function on directly. Defining onEditFormSubmit() in TimersDashboard Last step with this pipeline is to define and pass down the submit function for edit forms in TimersDashboard. For creates, we have a function that creates a new timer object with the specified attributes and we append this new object to the end of the timers array in the state. For updates, we need to hunt through the timers array until we find the timer object that is being updated. As mentioned in the last chapter, the state object cannot be updated directly. We have to use setState(). Therefore, we’ll use map() to traverse the array of timer objects. If the timer’s id matches that of the form submitted, we’ll return a new object that contains the timer with the updated attributes. Otherwise we’ll just return the original timer. This new array of timer objects will be passed to setState(): // Inside TimersDashboard handleEditFormSubmit: function (attrs) { this.updateTimer(attrs); }, createTimer: function (timer) { const t = helpers.newTimer(timer); this.setState({ timers: this.state.timers.concat(t), }); }, Components 75 updateTimer: function (attrs) { this.setState({ timers: this.state.timers.map((timer) => { if (timer.id === attrs.id) { return Object.assign({}, timer, { title: attrs.title, project: attrs.project, }); } else { return timer; } }), }); }, render: function () { We pass this down as a prop inside render(): { /* Inside TimersDashboard.render() */} Note that we can call map() on this.state.timers from within the JavaScript object we’re passing to setState(). This is an often used pattern. The call is evaluated and then the property timers is set to the result. Inside of the map() function we check if the timer matches the one being updated. If not, we just return the timer. Otherwise, we use Object#assign() to return a new object with the timer’s updated attributes. As we did with ToggleableTimerForm and handleCreateFormSubmit, we pass down handleEditFormSubmit as the prop onFormSubmit. TimerForm calls this prop, oblivious to the fact that this function is entirely different when it is rendered underneath EditableTimer as opposed to ToggleableTimerForm. ES6: Object#assign We will use Object#assign() frequently throughout this book to create new objects as opposed to modifying existing ones. Object#assign() accepts any number of objects as arguments. When the function receives two arguments, it copies the properties of the second object onto the first, like so: Components 76 const coffee = { }; const noCream = { cream: false }; const noMilk = { milk: false }; Object.assign(coffee, noCream); // coffee is now: `{ cream: false }` Object.assign(coffee, noMilk); // coffee is now: `{ cream: false, milk: false }` Throughout this book, we’ll often pass three arguments to Object.assign(), as seen in TimersDashboard. The first argument is a new JavaScript object, the one that Object#assign() will ultimately return. The second is the object whose properties we’d like to build off of. The last is the changes we’d like to apply: const coffeeWithMilk = Object.assign({}, coffee, { milk: true }); // coffeeWithMilk is: `{ cream: false, milk: true }` // coffee was not modified: `{ cream: false, milk: false }` Modifying JavaScript objects Note that we could have gotten away with updating the timer object directly, like so: // ... if (timer.id === attrs.id) { timer.title = attrs.title; timer.project = attrs.project; return timer; } In JavaScript objects are passed by reference. As such, the code above would have modified the timer object sitting inside this.state.timers, not a copy of that object. We still call setState(), though, so React would be notified about this change. It is unlikely negative consequences would have emerged. However, manipulating objects in this manner is generally bad practice and something we will avoid throughout this book. The general rule we will adhere to: unless a function owns an object (it declared the object), it should refrain from making any edits to it. To understand why this practice is dangerous, let’s reflect on what could happen in this specific instance. Consider how many different components in the app reference the state object. Every single prop corresponding to a timer is referencing properties on this object. If we make direct edits to this object, those changes will ripple out into all sorts of different areas in the code. Components 77 Imagine if instead of immediately calling setState(), we had some sort of delay after modifying the state object, like a call to a web service. If any children happened to re-render during that period (like if their own state changed), the props they would use would be the new values, even though we weren’t yet prepared for them to receive this change. This could lead to all sorts of odd, unpredictable behavior. We will explore the related concept of pure functions in a future chapter. Both of the forms are wired up! Save app.js, reload the page, and try both creating and updating timers. You can also click “Cancel” on an open form to close it: The rest of our work resides within the timer. We need to: • Wire up the trash button (deleting a timer) • Implement the start/stop buttons and the timing logic itself At that point, we’ll have a complete server-less solution. Try it yourself: Before moving on to the next section, see how far you can get wiring up the trash button by yourself. Move ahead afterwards and verify your solution is sound. Components 78 Deleting timers Adding the event handler to Timer In Timer, we define a function to handle trash button click events: const Timer = React.createClass({ handleTrashClick: function () { this.props.onTrashClick(this.props.id); }, render: function () { // ... And then use onClick to connect that function to the trash icon: {/* Inside Timer.render() */}
We’ve yet to define the function that will be set as the prop onTrashClick(). But you can imagine that when this event reaches the top (TimersDashboard), we’re going to need the id to sort out which timer is being deleted. handleTrashClick() provides the id to this function. Routing through EditableTimer EditableTimer just proxies the function: Components 79 // Inside EditableTimer } else { return ( ); } Routing through EditableTimerList As does EditableTimerList: // Inside EditableTimerList.render() const timers = this.props.timers.map((timer) => ( )); Implementing the delete function in TimersDashboard The last step is to define the function in TimersDashboard that deletes the desired timer from the state array. There are many ways to accomplish this in JavaScript. Don’t sweat it if your solution was not the same or if you didn’t quite work one out. We add our handler function that we ultimately pass down as a prop: Components 80 // Inside TimersDashboard handleEditFormSubmit: function (attrs) { this.updateTimer(attrs); }, handleTrashClick: function (timerId) { this.deleteTimer(timerId); }, deleteTimer() uses Array’s filter() method to return a new array with the timer object that has an id matching timerId removed: // Inside TimersDashboard deleteTimer: function (timerId) { this.setState({ timers: this.state.timers.filter(t => t.id !== timerId), }); }, render: function () { Finally, we pass down handleTrashClick() as a prop: {/* Inside TimersDashboard.render() */} Array’s filter() method accepts a function that is used to “test” each element in the array. It returns a new array containing all the elements that “passed” the test. If the function returns true, the element is kept. Save app.js and reload the app. Low and behold, you can delete timers: Components 81 Adding timing functionality Create, update, and delete (CRUD) capability is now in place for our timers. The next challenge: making these timers functional. There are several different ways we can implement a timer system. The simplest approach would be to have a function update the elapsed property on each timer every second. But this is severely limited. What happens when the app is closed? The timer should continue “running.” This is why we’ve included the timer property runningSince. A timer is initialized with elapsed equal to 0. When a user clicks “Start”, we do not increment elapsed. Instead, we just set runningSince to the start time. We can then use the difference between the start time and the current time to render the time for the user. When the user clicks “Stop”, the difference between the start time and the current time is added to elapsed. runningSince is set to null. Therefore, at any given time, we can derive how long the timer has been running by taking Date.now() - runningSince and adding it to the total accumulated time (elapsed). We’ll calculate this inside the Timer component. For the app to truly feel like a running timer, we want React to constantly perform this operation and re-render the timers. But elapsed and runningSince will not be changing while the timer is running. So the one mechanism we’ve seen so far to trigger a render() call will not be sufficient. Instead, we can use React’s forceUpdate() method. This forces a React component to re-render. We can call it on an interval to yield the smooth appearance of a live timer. Components 82 Adding a forceUpdate() interval to Timer helpers.renderElapsedString() accepts an optional second argument, runningSince. It will add the delta of Date.now() - runningSince to elapsed and use the function millisecondsToHuman() to return a string formatted as HH:MM:SS. We will establish an interval to run forceUpdate() after the component mounts: const Timer = React.createClass({ componentDidMount: function () { this.forceUpdateInterval = setInterval(() => this.forceUpdate(), 50); }, componentWillUnmount: function () { clearInterval(this.forceUpdateInterval); }, handleTrashClick: function () { this.props.onTrashClick(this.props.id); }, render: function () { const elapsedString = helpers.renderElapsedString( this.props.elapsed, this.props.runningSince ); return ( In componentDidMount(), we use the JavaScript function setInterval(). This will invoke the function forceUpdate() once every 50 ms, causing the component to re-render. We set the return of setInterval() to this.forceUpdateInterval. In componentWillUnmount(), we use clearInterval() to stop the interval this.forceUpdateInterval. componentWillUnmount() is called before a component is removed from the app. This will happen if a timer is deleted. We want to ensure we do not continue calling forceUpdate() after the timer has been removed from the page. React will throw errors. setInterval() accepts two arguments. The first is the function you would like to call repeatedly. The second is the interval on which to call that function (in milliseconds). setInterval() returns a unique interval ID. You can pass this interval ID to clearInterval() at any time to halt the interval. You might ask: Wouldn’t it be more efficient if we did not continuously call forceUpdate() on timers that are not running? Indeed, we would save a few cycles. But it would not be worth the added code complexity. React will call render() which performs some inexpensive operations in JavaScript. It will then compare this result to the previous call to render() and see that nothing has changed. It stops there — it won’t attempt any DOM manipulation. Components 83 The 50 ms interval was not derived scientifically. Selecting an interval that’s too high will make the timer look unnatural. It would jump unevenly between values. Selecting an interval that’s too low would just be an unnecessary amount of work. A 50 ms interval looks good to humans and is ages in computerland. Try it out Save app.js and reload. The first timer should be running. We’ve begun to carve out the app’s real utility! We need only wire up the start/stop button and our server-less app will be feature complete. Add start and stop functionality The action button at the bottom of each timer should display “Start” if the timer is paused and “Stop” if the timer is running. It should also propagate events when clicked, depending on if the timer is being stopped or started. We could build all of this functionality into Timer. We could have Timer decide to render one HTML snippet or another depending on if it is running. But that would be adding more responsibility and complexity to Timer. Instead, let’s make the button its own React component. Add timer action events to Timer Let’s modify Timer, anticipating a new component called TimerActionButton. This button just needs to know if the timer is running. It also needs to be able to propagate two events, onStartClick() and onStopClick(). These events will eventually need to make it all the way up to TimersDashboard, which can modify runningSince on the timer. First, the event handlers: // Inside Timer componentWillUnmount: function () { clearInterval(this.forceUpdateInterval); }, handleStartClick: function () { this.props.onStartClick(this.props.id); }, handleStopClick: function () { this.props.onStopClick(this.props.id); }, // ... Then, inside render(), we’ll declare TimerActionButton at the bottom of the outermost div: Components 84 {/* At the bottom of `Timer.render()`` */}
); We use the same technique used in other click-handlers: onClick on the HTML element specifies a handler function in the component that invokes a prop-function, passing in the timer’s id. We use !! here to derive the boolean prop timerIsRunning for TimerActionButton. !! returns false when runningSince is null. Create TimerActionButton Create the TimerActionButton component now: const TimerActionButton = React.createClass({ render: function () { if (this.props.timerIsRunning) { return (
Stop
); } else { return (
Start
); } }, }); Components 85 We render one HTML snippet or another based on this.props.timerIsRunning. You know the drill. Now we run these events up the component hierarchy, all the way up to TimersDashboard where we’re managing state: Run the events through EditableTimer and EditableTimerList First EditableTimer: // Inside EditableTimer } else { return ( ); } And then EditableTimerList: // Inside EditableTimerList const timers = this.props.timers.map((timer) => ( )); Components 86 Finally, we define these functions in TimersDashboard. They should hunt through the state timers array using map, setting runningSince appropriately when they find the matching timer. First we define the handling functions: // Inside TimersDashboard handleTrashClick: function (timerId) { this.deleteTimer(timerId); }, handleStartClick: function (timerId) { this.startTimer(timerId); }, handleStopClick: function (timerId) { this.stopTimer(timerId); }, And then startTimer() and stopTimer(): deleteTimer: function (timerId) { this.setState({ timers: this.state.timers.filter(t => t.id !== timerId), }); }, startTimer: function (timerId) { const now = Date.now(); this.setState({ timers: this.state.timers.map((timer) => { if (timer.id === timerId) { return Object.assign({}, timer, { runningSince: now, }); } else { return timer; } }), }); }, stopTimer: function (timerId) { const now = Date.now(); this.setState({ timers: this.state.timers.map((timer) => { Components 87 if (timer.id === timerId) { const lastElapsed = now - timer.runningSince; return Object.assign({}, timer, { elapsed: timer.elapsed + lastElapsed, runningSince: null, }); } else { return timer; } }), }); }, render: function () { Finally, we pass them down as props: {/* Inside TimerDashboard.render() */} When startTimer comes across the relevant timer within its map call, it sets the property runningSince to the current time. stopTimer calculates lastElapsed, the amount of time that the timer has been running for since it was started. It adds this amount to elapsed and sets runningSince to null, “stopping” the timer. Try it out Save app.js, reload, and behold! You can now create, update, and delete timers as well as actually use them to time things: Components 88 This is excellent progress. But, without a connection to a server, our app is ephemeral. If we refresh the page, we lose all of our timer data. Our app does not have any persistence. A server can give us persistence. We’ll have our server write all changes to timer data to a file. Instead of hard-coding state inside of the TimersDashboard component, when our app loads it will query the server and construct its timer state based on the data the server provides. We’ll then have our React app notify the server about any state changes, like when a timer is started. Communicating with a server is the last big major building block you’ll need to develop and distribute real-world web applications with React. Methodology review While building our time-logging app, we learned and applied a methodology for building React apps. Again, those steps were: 1. Break the app into components We mapped out the component structure of our app by examining the app’s working UI. We then applied the single-responsibility principle to break components down so that each had minimal viable functionality. 2. Build a static version of the app Our bottom-level (user-visible) components rendered HTML based on static props, passed down from parents. 3. Determine what should be stateful We used a series of questions to deduce what data should be stateful. This data was represented in our static app as props. 4. Determine in which component each piece of state should live We used another series of questions to determine which component should own each piece of state. TimersDashboard owned timer state data and ToggleableTimerForm and EditableTimer both held state pertaining to whether or not to render a TimerForm. Components 89 5. Hard-code initial states We then wrote state-owners’ getInitialState functions with hard-coded values. 6. Add inverse data flow We added interactivity by decorating buttons with onClick handlers. These called functions that were passed in as props down the hierarchy from whichever component owned the relevant state being manipulated. The final step is 7. Add server communication. We’ll tackle this in the next chapter. Chapter Exercises 1. Modify the Timer component so that the “Edit” and “Trash” buttons only display when the mouse is hovering over a given timer. onMouseEnter and onMouseLeave will serve as your event attributes in much the same way as onClick has. 2. Add validation to the title and project fields on timers. They should not be blank. An error message should be displayed above the field if the validation fails. Check out the forms page on Semantic UI for the HTML for form errors: http://semanticui.com/collections/form.html#field-error²⁶. ²⁶http://semantic-ui.com/collections/form.html#field-error Components & Servers Introduction In the last chapter, we used a methodology to construct a React app. State management of timers takes place in the top-level component TimersDashboard. As in all React apps, data flows from the top down through the component tree to leaf components. Leaf components communicate events to state managers by calling prop-functions. At the moment, TimersDashboard has a hard-coded initial state. Any mutations to the state will only live as long as the browser window is open. That’s because all state changes are happening in-memory inside of React. We need our React app to communicate with a server. The server will be in charge of persisting the data. In this app, data persistence happens inside of a file, data.json. EditableTimer and ToggleableTimerForm also have hard-coded initial state. But because this state is just whether or not their forms are open, we don’t need to communicate these state changes to the server. We’re OK with the forms starting off closed every time the app boots. Preparation To help you get familiar with the API for this project and working with APIs in general, we have a short section where we make requests to the API outside of React. curl We’ll use a tool called curl to make more involved requests from the command line. OS X users should already have curl installed. Windows users can download and install curl here: https://curl.haxx.se/download.html²⁷. server.js Included in the root of your project folder is a file called server.js. This is a Node.js server specifically designed for our time-tracking app. You don’t have to know anything about Node.js or about servers in general to work with the server we’ve supplied. We’ll provide the guidance that you need. ²⁷https://curl.haxx.se/download.html Components & Servers 91 server.js uses the file data.json as its “store.” The server will read and write to this file to persist data. You can take a look at that file to see the initial state of the store that we’ve provided. server.js will return the contents of data.json when asked for all items. When notified, the server will reflect any updates, deletes, or timer stops and starts in data.json. This is how data will be persisted even if the browser is reloaded or closed. Before we start working with the server, let’s briefly cover its API. Again, don’t be concerned if this outline is a bit perplexing. It will hopefully become clearer as we start writing some code. The Server API Our ultimate goal in this chapter is to replicate state changes on the server. We’re not going to move all state management exclusively to the server. Instead, the server will maintain its state (in data.json) and React will maintain its state (in this case, within this.state in TimersDashboard). We’ll demonstrate later why keeping state in both places is desirable. TimersDashboard communicates with the server If we perform an operation on the React (“client”) state that we want to be persisted, then we also need to notify the server of that state change. This will keep the two states in sync. We’ll consider these our “write” operations. The write operations we want to send to the server are: • A timer is created Components & Servers 92 • A timer is updated • A timer is deleted • A timer is started • A timer is stopped We’ll have just one read operation: requesting all of the timers from the server. HTTP APIs This section assumes a little familiarity with HTTP APIs. If you’re not familiar with HTTP APIs, you may want to read up on them²⁸ at some point. However, don’t be deterred from continuing with this chapter for the time being. Essentially what we’re doing is making a “call” from our browser out to a local server and conforming to a specified format. text/html endpoint GET / This entire time, server.js has actually been responsible for serving the app. When your browser requests localhost:3000/, the server returns the file index.html. index.html loads in all of our JavaScript/React code. Note that React never makes a request to the server at this path. This is just used by the browser to load the app. React only communicates with the JSON endpoints. JSON endpoints data.json is a JSON document. As touched on in the last chapter, JSON is a format for storing human-readable data objects. We can serialize JavaScript objects into JSON. This enables JavaScript objects to be stored in text files and transported over the network. data.json contains an array of objects. While not strictly JavaScript, the data in this array can be readily loaded into JavaScript. In server.js, we see lines like this: ²⁸http://www.andrewhavens.com/posts/20/beginners-guide-to-creating-a-rest-api/ Components & Servers 93 fs.readFile(DATA_FILE, function(err, data) { const timers = JSON.parse(data); // ... }); data is a string, the JSON. JSON.parse() converts this string into an actual JavaScript array of objects. GET /api/timers Returns a list of all timers. POST /api/timers Accepts a JSON body with title, project, and id attributes. Will insert a new timer object into its store. POST /api/timers/start Accepts a JSON body with the attribute id and start (a timestamp). Hunts through its store and finds the timer with the matching id. Sets its runningSince to start. POST /api/timers/stop Accepts a JSON body with the attribute id and stop (a timestamp). Hunts through its store and finds the timer with the matching id. Updates elapsed according to how long the timer has been running (stop - runningSince). Sets runningSince to null. PUT /api/timers Accepts a JSON body with the attributes id and title and/or project. Hunts through its store and finds the timer with the matching id. Updates title and/or project to new attributes. DELETE /api/timers Accepts a JSON body with the attribute id. Hunts through its store and deletes the timer with the matching id. Playing with the API If your server is not booted, make sure to boot it: npm run server You can visit the endpoint /api/timers endpoint in your browser and see the JSON response (localhost:3000/api/timers²⁹). When you visit a new URL in your browser, your browser makes a GET request. So our browser calls GET /api/timers and the server returns all of the timers: ²⁹localhost:3000/api/timers Components & Servers 94 Note that the server stripped all of the extraneous whitespace in data.json, including newlines, to keep the payload as small as possible. Those only exist in data.json to make it human-readable. We can only easily use the browser to make GET requests. For writing data — like starting and stopping timers — we’ll have to make POST, PUT, or DELETE requests. We’ll use curl to play around with writing data. Run the following command from the command line: curl -X GET localhost:3000/api/timers The -X flag specifies which HTTP method to use. It should return a response that looks a bit like this: [{"title":"Mow the lawn","project":"House Chores","elapsed":5456099,"id":"0a4a79\ cb-b06d-4cb1-883d-549a1e3b66d7"},{"title":"Clear paper jam","project":"Office Ch\ ores","elapsed":1273998,"id":"a73c1d19-f32d-4aff-b470-cea4e792406a"},{"title":"P\ onder origins of universe","project":"Life Chores","id":"2c43306e-5b44-4ff8-8753\ -33c35adbd06f","elapsed":11750,"runningSince":"1456225941911"}] You can start one of the timers by issuing a PUT request to the /api/timers/start endpoint. We need to send along the id of one of the timers and a start timestamp: Components & Servers 95 curl -X POST \ -H 'Content-Type: application/json' \ -d '{"start":1456468632194,"id":"a73c1d19-f32d-4aff-b470-cea4e792406a"}' \ localhost:3000/api/timers/start The -H flag sets a header for our HTTP request, Content-Type. We’re informing the server that the body of the request is JSON. The -d flag sets the body of our request. Inside of single-quotes '' is the JSON data. When you press enter, curl will quickly return without any output. The server doesn’t return anything on success for this endpoint. If you open up data.json, you will see that the timer you specified now has a runningSince property, set to the value we specified as start in our request. If you’d like, you can play around with the other endpoints to get a feel for how they work. Just be sure to set the appropriate method with -X and to pass along the JSON Content-Type for the write endpoints. We’ve written a small library, client, to aid you in interfacing with the API in JavaScript. Note that the backslash \ above is only used to break the command out over multiple lines for readability. Tool tip: jq If you want to parse and process JSON on the command line, we highly recommend the tool “jq.” You can pipe curl responses directly into jq to have the response pretty-formatted: curl -X GET localhost:3000/api/timers | jq '.' You can also do some powerful manipulation of JSON, like iterating over all objects in the response and returning a particular field. In this example, we extract just the id property of every object in an array: curl -X GET localhost:3000/api/timers | jq '.[] | { id }' You can download jq here: https://stedolan.github.io/jq/. https://stedolan.github.io/jq/ Components & Servers 96 Loading state from the server Right now, we set initial state in TimersDashboard by hardcoding a JavaScript object, an array of timers. Let’s modify this function to load data from the server instead. We’ve written the client library that your React app will use to interact with the server, client. The library is defined in public/client.js. We’ll use it first and then take a look at how it works in the next section. The GET /api/timers endpoint provides a list of all timers, as represented in data.json. We can use client.getTimers() to call this endpoint from our React app. We’ll do this to “hydrate” the state kept by TimersDashboard. When we call client.getTimers(), the network request is made asynchronously. The function call itself is not going to return anything useful: // Wrong // `getTimers()` does not return the list of timers const timers = client.getTimers(); Instead, we can pass getTimers() a success function. getTimers() will invoke that function after it hears back from the server if the server successfully returned a result. getTimers() will invoke the function with a single argument, the list of timers returned by the server: // Passing `getTimers()` a success function client.getTimers((serverTimers) => ( // do something with the array of timers, `serverTimers` )); client.getTimers() uses the Fetch API, which we cover in the next section. For our purposes, the important thing to know is that when getTimers() is invoked, it fires off the request to the server and then returns control flow immediately. The execution of our program does not wait for the server’s response. This is why getTimers() is called an asynchronous function. The success function we pass to getTimers() is called a callback. We’re saying: “When you finally hear back from the server, if it’s a successful response, invoke this function.” This asynchronous paradigm ensures that execution of our JavaScript is not blocked by I/O. While our initial instinct might be to try to load the timers from the server in getInitialState(), we don’t want a request to the server to hold up the mounting of our app. Instead, we should render a blank canvas for the app by setting timers in state to []. This will allow all components to mount and perform their initial render. Then, we can populate the app by making a request to the server and setting the state: Components & Servers 97 const TimersDashboard = React.createClass({ getInitialState: function () { return { timers: [], }; }, componentDidMount: function () { this.loadTimersFromServer(); setInterval(this.loadTimersFromServer, 5000); }, loadTimersFromServer: function () { client.getTimers((serverTimers) => ( this.setState({ timers: serverTimers }) ) ); }, // ... A timeline is the best medium for illustrating what happens: 1. Before initial render React calls getInitialState() on TimersDashboard. An object with the property timers, a blank array, is returned. 2. The initial render React then calls render() on TimersDashboard. In order for the render to complete, EditableTimerList and ToggleableTimerForm — its two children — must be rendered. 3. Children are rendered EditableTimerList has its render method called. Because it was passed a blank data array, it simply produces the following HTML output:
ToggleableTimerForm renders its HTML, which is the “+” button. 4. Initial render is finished With its children rendered, the initial render of TimersDashboard is finished and the HTML is written to the DOM. 5. componentDidMount is invoked Now that the component is mounted, componentDidMount() is called on TimersDashboard. Components & Servers 98 This method calls loadTimersFromServer(). In turn, that function calls client.getTimers(). That will make the HTTP request to our server, requesting the list of timers. When client hears back, it invokes our success function. On invocation, the success function is passed one argument, serverTimers. This is the array of timers returned by the server. We then call setState(), which will trigger a new render. The new render populates our app with EditableTimer children and all of their children. The app is fully loaded and at an imperceptibly fast speed for the end user. We also do one other interesting thing in componentDidMount. We use setInterval() to ensure loadTimersFromServer() is called every 5 seconds. While we will be doing our best to mirror state changes between client and server, this hard-refresh of state from the server will ensure our client will always be correct should it shift from the server. The server is considered the master holder of state. Our client is a mere replica. This becomes incredibly powerful in a multi-instance scenario. If you have two instances of your app running — in two different tabs or two different computers — changes in one will be pushed to the other within five seconds. Try it out Let’s have fun with this now. Save app.js and reload the app. You should see a whole new list of timers, driven by data.json. Any action you take will be wiped out within five seconds. Every five seconds, state is restored from the server. For instance, try deleting a timer and witness it resiliently spring back unscathed. Because we’re not telling the server about these actions, its state remains unchanged. On the flip-side, you can try modifying data.json. Notice how any modifications to data.json will be propagated to your app in under five seconds. Neat. We’re loading the initial state from the server. We have an interval function in place to ensure the client app’s state does not drift from the server’s in a multi-instance scenario. We’ll need to inform our server of the rest of our state changes: creates, updates (including starts and stops), and deletes. But first, let’s pop open the logic behind client to see how it works. While it is indeed neat that changes to our server data is seamlessly propagated to our view, in certain applications — like messaging — five seconds is almost an eternity. We’ll cover the concept of long-polling in a future app. Long-polling enables changes to be pushed to clients near instantly. client If you open up client.js, the first method defined in the library is getTimers(): Components & Servers 99 function getTimers(success) { return fetch('/api/timers', { headers: { Accept: 'application/json', }, }).then(checkStatus) .then(parseJSON) .then(success); } We are using the new Fetch API to perform all of our HTTP requests. Fetch’s interface should look relatively familiar if you’ve ever used XMLHttpRequest or jQuery’s ajax(). Fetch Until Fetch, JavaScript developers had two options for making web requests: Use XMLHttpRequest which is supported natively in all browsers or import a library that provides a wrapper around it (like jQuery’s ajax()). Fetch provides a better interface than XMLHttpRequest. And while Fetch is still undergoing standardization, it is already supported by a few major browsers. At the time of writing, Fetch is turned on by default in Firefox 39 and above and Chrome 42 and above. Until Fetch is more widely adopted by browsers, it’s a good idea to include the library just in case. We’ve already done so inside index.html: As we can see in client.getTimers(), fetch() accepts two arguments: • The path to the resource we want to fetch • An object of request parameters By default, Fetch makes a GET request, so we’re telling Fetch to make a GET request to /api/timers. We also pass along one parameter: headers, the HTTP headers in our request. We’re telling the server we’ll accept only a JSON response. Attached to the end of our call to fetch(), we have a chain of .then() statements: Components & Servers 100 }).then(checkStatus) .then(parseJSON) .then(success); To understand how this works, let’s first review the functions that we pass to each .then() statement: • checkStatus(): This function is defined inside of client.js. It checks if the server returned an error. If the server returned an error, checkStatus() logs the error to the console. • parseJSON(): This function is also defined inside of client.js. It takes the response object emitted by fetch() and returns a JavaScript object. • success(): This is the function we pass as an argument to getTimers(). getTimers() will invoke this function if the server successfully returned a response. Fetch returns a promise. While we won’t go into detail about promises, as you can see here a promise allows you to chain .then() statements. We pass each .then() statement a function. What we’re essentially saying here is: “Fetching the timers from /api/timers then check the status code returned by the server. Then, extract the JavaScript object from the response. Then, pass that object to the success function.” At each stage of the pipeline, the result of the previous statement is passed as the argument to the next one: 1. When checkStatus() is invoked, it’s passed a Fetch response object that fetch() returns. 2. checkStatus(), after verifying the response, returns the same response object. 3. parseJSON() is invoked and passed the response object returned by checkStatus(). 4. parseJSON() returns the JavaScript array of timers returned by the server. 5. success() is invoked with the array of timers returned by parseJSON(). We could attach an infinite number of .then() statements to our pipeline. This pattern enables us to chain multiple function calls together in an easy-to-read format that supports asynchronous functions like fetch(). It’s OK if you’re still uncomfortable with the concept of promises. We’ve written all the client code for this chapter for you, so you won’t have trouble completing this chapter. You can come back afterwards to play around with client.js and get a feel for how it works. You can read more about JavaScript’s Fetch here³⁰ and promises here³¹. ³⁰https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API ³¹https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise Components & Servers 101 Looking at the rest of the functions in client.js, you’ll note the methods contain much of the same boilerplate with small differences based on the endpoint of the API we are calling. We just looked at getTimers() which demonstrates reading from the server. We’ll look at one more function, one that writes to the server. startTimer() makes a POST request to the /api/timers/start endpoint. The server needs the id of the timer and the start time. That request method looks like: function startTimer(data) { return fetch('/api/timers/start', { method: 'post', body: JSON.stringify(data), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, }).then(checkStatus); } In addition to headers, the request parameters object that we pass to fetch() has two more properties: method: 'post', body: JSON.stringify(data), Those are: • method: The HTTP request method. fetch() defaults to a GET request, so we specify we’d like a POST here. • body: The body of our HTTP request, the data we’re sending to the server. startTimer() expects an argument, data. This is the object that will be sent along in the body of our request. It contains the properties id and start. An invocation of startTimer() might look like this: Components & Servers 102 // Example invocation of `startTimer()` startTimer( { id: "bc5ea63b-9a21-4233-8a76-f4bca9d0a042", start: 1455584369113, } ); In this example, the body of our request to the server will look like this: { "id": "bc5ea63b-9a21-4233-8a76-f4bca9d0a042", "start": 1455584369113 } The server will extract the id and start timestamp from the body and “start” the timer. We don’t pass startTimers() a success function. Our app does not need data from the server for this request and indeed our server will not return anything besides an “OK” anyway. getTimers() is our only read operation and therefore the only one we’ll pass a success function. The rest of our calls to the server are writes. Let’s implement those now. Sending starts and stops to the server We can use the methods startTimer() and stopTimer() on client to make calls to the appropriate endpoints on the server. We just need to pass in an object that includes the id of the timer as well as the time it was started/stopped: // Inside TimersDashboard // ... startTimer: function (timerId) { const now = Date.now(); this.setState({ timers: this.state.timers.map((timer) => { if (timer.id === timerId) { return Object.assign({}, timer, { runningSince: now, }); } else { return timer; Components & Servers 103 } }), }); client.startTimer( { id: timerId, start: now } ); }, stopTimer: function (timerId) { const now = Date.now(); this.setState({ timers: this.state.timers.map((timer) => { if (timer.id === timerId) { const lastElapsed = now - timer.runningSince; return Object.assign({}, timer, { elapsed: timer.elapsed + lastElapsed, runningSince: null, }); } else { return timer; } }), }); client.stopTimer( { id: timerId, stop: now } ); }, render: function () { // ... You might ask: Why do we still manually make the state change within React? Can’t we just inform the server of the action taken and then update state based on the server, the source of truth? Indeed, the following implementation is valid: Components & Servers 104 startTimer: function (timerId) { const now = Date.now(); client.startTimer( { id: timerId, start: now } ).then(loadTimersFromServer); }, We can chain a .then() to startTimer() as that function returns our original promise object. The last stage of the startTimer() pipeline would then be invoking the function loadTimersFromServer(). So immediately after the server processes our start timer request, we would make a subsequent request asking for the latest list of timers. This response would contain the now-running timer. React’s state updates and the running timer would then be reflected in the UI. Again, this is valid. However, the user experience will leave something to be desired. Right now, clicking the start/stop button gives instantaneous feedback because the state changes locally and React immediately re-renders. If we waited to hear back from the server, there might be a noticeable delay between the action (mouse click) and the response (timer starts running). You can try it yourself locally, but the delay would be most noticeable if the request had to go out over the internet. What we’re doing here is called optimistic updating. We’re updating the client locally before waiting to hear from the server. This duplicates our state update efforts, as we perform updates on both the client and the server. But it makes our app as responsive as possible. The “optimism” we have here is that the request will succeed and not fail with an error. Using the same pattern as we did with starts and stops, see if you can implement creates, updates, and deletes on your own. Come back and compare your work with the next section. Optimistic updating: Validations Whenever we optimistic update, we always try to replicate whatever restrictions the server would have. This way, our client state changes under the same conditions as our server state. For example, imagine if our server enforced that a timer’s title cannot contain symbols. But the client did not enforce such a restriction. What would happen? A user has a timer named Gardening. He feels a bit cheeky and renames it Gardening :P. The UI immediately reflects his changes, displaying Gardening :P as the new name of the timer. Satisfied, the user is about to get up and grab his shears. But wait! His timer’s name suddenly snaps back to Gardening. To successfully pull off eager updating, we must diligently replicate the code that manages state changes on both the client and the server. Furthermore, in a production app we should surface any Components & Servers 105 errors the request to the server returns in the event that there is an inconsistency in the code or a fluke (the server is down). Implementing this feature for this app is left as an exercise at the end of the chapter. Sending creates, updates, and deletes to the server // Inside TimersDashboard // ... createTimer: function (timer) { const t = helpers.newTimer(timer); this.setState({ timers: this.state.timers.concat(t), }); client.createTimer(t); }, updateTimer: function (attrs) { this.setState({ timers: this.state.timers.map((timer) => { if (timer.id === attrs.id) { return Object.assign({}, timer, { title: attrs.title, project: attrs.project, }); } else { return timer; } }), }); client.updateTimer(attrs); }, deleteTimer: function (timerId) { this.setState({ timers: this.state.timers.filter(t => t.id !== timerId), }); client.deleteTimer( { id: timerId } Components & Servers 106 ); }, startTimer: function (timerId) { // ... Recall that, in createTimer() and updateTimer() respectively, the timer and attrs objects contain an id property, as required by the server. For creates, we need to send a full timer object. It should have an id, a title, and a project. For updates, we can send an id along with just whatever attributes are being updated. Right now, we always send along title and project regardless of what has changed. But it’s worth noting this difference as it’s reflected in the variable names that we are using (timer vs attrs). Give it a spin We are now sending all of our state changes to the server. Save app.js and reload the app. Add some timers, start some timers, and refresh and note that everything is persisted. You can even make changes to your app in one browser tab and see the changes propagate to another tab. Next up We’ve worked through a reusable methodology for building React apps and now have an understanding of how we connect a React app to a web server. Armed with these concepts, you’re already equipped to build a variety of dynamic web applications. In imminent chapters, we’ll cover a variety of different component types that you encounter across the web (like forms and date pickers). We’ll also explore state management paradigms for more complex applications. Stay tuned! Components & Servers 107 Chapter Exercises Right now, if a write to the server fails the app does not surface the issue. There are few ways we could surface that there was an error. For both exercises below, note that checkStatus() will throw an error if the server returns a bad response (a non-200). In a promise chain, you can add a .catch() that will be executed if a call in the chain has emitted an error. You can have each client method accept an additional argument, onError. To have this function invoked in the case a server returns an error, you can insert a .catch() call after .then(checkStatus), like this: function createTimer(data, onError) { return fetch('/api/timers', { // request parameters here }).then(checkStatus) .catch(onError); } 1. Modify how TimersDashboard uses client such that an error is displayed to the user if communication with the server fails. You can use a Semantic UI “Segment” to contain the error message: http://semanticui.com/elements/segment.html#emphasis³² 2. Building off of the first exercise, modify TimerForm so that it accepts new props, fieldErrors. TimersDashboard can maintain this state. If TimersDashboard fails to communicate with the server, it should pop the form submission that failed back open and pass it the errors so that they can be displayed to the user. Check out Semantic UI’s form field errors for the HTML: http://semanticui.com/collections/form.html#field-error³³ ³²http://semantic-ui.com/elements/segment.html#emphasis ³³http://semantic-ui.com/collections/form.html#field-error JSX and the Virtual DOM React Uses a Virtual DOM React works differently than many earlier front-end JavaScript frameworks in that instead of working with the browser’s DOM, it builds a virtual representation of the DOM. By virtual, we mean a tree of JavaScript objects that represent the “actual DOM”. More on this in a minute. In React, we do not directly manipulate the actual DOM. Instead, we must manipulate the virtual representation and let React take care of changing the browser’s DOM. As we’ll see in this chapter, this is a very powerful feature but it requires us to think differently about how we build web apps. Why Not Modify the Actual DOM? It’s worth asking: why do we need a Virtual DOM? Can’t we just use the “actual-DOM”? When we do “classic-“ (e.g. jQuery-) style web development, we would typically: 1. locate an element (using document.querySelector or document.getElementById) and then 2. modify that element directly (say, by calling .innerHTML() on the element). This style of development is problematic in that: • It’s hard to keep track of changes - it can become difficult keep track of current (and prior) state of the DOM to manipulate it into the form we need • It can be slow - modifying the actual-DOM is a costly operation, and modifying the DOM on every change can cause poor performance What is a Virtual DOM? The Virtual DOM was created to deal with these issues. But what is the Virtual DOM anyway? The Virtual DOM is a tree of JavaScript objects that represents the actual DOM. One of the interesting reasons to use the Virtual DOM is the API it gives us. When using the Virtual DOM we code as if we’re recreating the entire DOM on every update. JSX and the Virtual DOM 109 This idea of re-creating the entire DOM results in an easy-to-comprehend development model: instead of the developer keeping track of all DOM state changes, the developer simply returns the DOM they wish to see. React takes care of the transformation behind the scenes. This idea of re-creating the Virtual DOM every update might sound like a bad idea: isn’t it going to be slow? In fact, React’s Virtual DOM implementation comes with important performance optimizations that make it very fast. The Virtual DOM will: • use efficient diffing algorithms, in order to know what changed • update subtrees of the DOM simultaneously • batch updates to the DOM All of this results in an easy-to-use and optimized way to build web apps. Virtual DOM Pieces Again, when building a web app in React, we’re not working directly with the browser’s “actual DOM” directly, but instead a virtual representation of it. Our job is to provide React with enough information to build a JavaScript object that represents what the browser will render. But what does this Virtual DOM JavaScript object actually consist of? React’s Virtual DOM is a tree of ReactElements. Understanding the Virtual DOM, ReactElements, and how they interact with the “actual DOM” is a lot easier to understand by working through some examples, which we’ll do below. Q: Virtual DOM vs. Shadow DOM, are they the same thing? (A: No) Maybe you’ve heard of the “Shadow DOM” and you’re wondering, is the Shadow DOM the same thing as the Virtual DOM? The answer is no. The Virtual DOM is a tree of JavaScript objects that represent the real DOM elements. The Shadow DOM is a form of encapsulation on our elements. Think about using the


Older Post