|
| 1 | +## Introduction |
| 2 | + |
| 3 | +To illustrate the problems React Router is going to solve for you, let's build a small application without it. |
| 4 | + |
| 5 | +### Without React Router |
| 6 | + |
| 7 | +```js |
| 8 | +var About = React.createClass({/*...*/}); |
| 9 | +var Inbox = React.createClass({/*...*/}); |
| 10 | +var Home = React.createClass({/*...*/}); |
| 11 | + |
| 12 | +var App = React.createClass({ |
| 13 | + getInitialState() { |
| 14 | + return { |
| 15 | + route: window.location.hash.substr(1) |
| 16 | + }; |
| 17 | + }, |
| 18 | + |
| 19 | + componentDidMount() { |
| 20 | + window.addEventListener('hashchange', () => { |
| 21 | + this.setState({ |
| 22 | + route: window.location.hash.substr(1) |
| 23 | + }); |
| 24 | + }); |
| 25 | + }, |
| 26 | + |
| 27 | + render() { |
| 28 | + var Child; |
| 29 | + switch (this.state.route) { |
| 30 | + case '/about': Child = About; break; |
| 31 | + case '/inbox': Child = Inbox; break; |
| 32 | + default: Child = Home; |
| 33 | + } |
| 34 | + |
| 35 | + return ( |
| 36 | + <div> |
| 37 | + <h1>App</h1> |
| 38 | + <ul> |
| 39 | + <li><a href="#/about">About</a></li> |
| 40 | + <li><a href="#/inbox">Inbox</a></li> |
| 41 | + </ul> |
| 42 | + <Child/> |
| 43 | + </div> |
| 44 | + ) |
| 45 | + } |
| 46 | +}); |
| 47 | + |
| 48 | +React.render(<App />, document.body); |
| 49 | +``` |
| 50 | + |
| 51 | +As the hash portion of the URL changes, `<App>` will render a different `<Child>` by branching on `this.state.route`. Pretty straightforward stuff. But it gets complicated fast. |
| 52 | + |
| 53 | +Imagine now that `Inbox` has some nested UI at different URLs, maybe something like this master detail view: |
| 54 | + |
| 55 | +``` |
| 56 | +path: /inbox/messages/1234 |
| 57 | +
|
| 58 | ++---------+------------+------------------------+ |
| 59 | +| About | Inbox | | |
| 60 | ++---------+ +------------------------+ |
| 61 | +| Compose Reply Reply All Archive | |
| 62 | ++-----------------------------------------------+ |
| 63 | +|Movie tomorrow| | |
| 64 | ++--------------+ Subject: TPS Report | |
| 65 | +|TPS Report From: [email protected] | |
| 66 | ++--------------+ | |
| 67 | +|New Pull Reque| So ... | |
| 68 | ++--------------+ | |
| 69 | +|... | | |
| 70 | ++--------------+--------------------------------+ |
| 71 | +``` |
| 72 | + |
| 73 | +And maybe a stats page when not viewing a message: |
| 74 | + |
| 75 | +``` |
| 76 | +path: /inbox |
| 77 | +
|
| 78 | ++---------+------------+------------------------+ |
| 79 | +| About | Inbox | | |
| 80 | ++---------+ +------------------------+ |
| 81 | +| Compose Reply Reply All Archive | |
| 82 | ++-----------------------------------------------+ |
| 83 | +|Movie tomorrow| | |
| 84 | ++--------------+ 10 Unread Messages | |
| 85 | +|TPS Report | 22 drafts | |
| 86 | ++--------------+ | |
| 87 | +|New Pull Reque| | |
| 88 | ++--------------+ | |
| 89 | +|... | | |
| 90 | ++--------------+--------------------------------+ |
| 91 | +``` |
| 92 | + |
| 93 | +We'd have to make our URL parsing a lot smarter, and we would end up with a lot of code to figure out which branch of nested components to be rendered at any given URL: `App -> About`, `App -> Inbox -> Messages -> Message`, `App -> Inbox -> Messages -> Stats`, etc. |
| 94 | + |
| 95 | +### With React Router |
| 96 | + |
| 97 | +Let's refactor our app to use React Router. |
| 98 | + |
| 99 | +```js |
| 100 | +// first we import some components |
| 101 | +import { Router, Route, Link } from 'react-router'; |
| 102 | + |
| 103 | +// then we delete a bunch of code from `App` and add some `Link` |
| 104 | +// components |
| 105 | +var App = React.createClass({ |
| 106 | + render() { |
| 107 | + return ( |
| 108 | + <div> |
| 109 | + <h1>App</h1> |
| 110 | + {/* change the <a>s to <Links>s */} |
| 111 | + <ul> |
| 112 | + <li><Link to="/about">About</Link></li> |
| 113 | + <li><Link to="/inbox">Inbox</Link></li> |
| 114 | + </ul> |
| 115 | + |
| 116 | + {/* |
| 117 | + next we replace `<Child>` with `this.props.children` |
| 118 | + the router will figure out the children for us |
| 119 | + */} |
| 120 | + {this.props.children} |
| 121 | + </div> |
| 122 | + ) |
| 123 | + } |
| 124 | +}); |
| 125 | + |
| 126 | +// Finally we render a Router component with some Routes. |
| 127 | +// It does all the fancy routing stuff for us. |
| 128 | +React.render(( |
| 129 | + <Router> |
| 130 | + <Route path="/" component={App}> |
| 131 | + <Route path="about" component={About} /> |
| 132 | + <Route path="inbox" component={Inbox} /> |
| 133 | + </Route> |
| 134 | + </Router> |
| 135 | +), document.body); |
| 136 | +``` |
| 137 | + |
| 138 | +React Router knows how to build nested UI for us, so we don't have to manually figure out which `<Child>` component to render. If you're not digging the JSX route config you can use plain objects instead: |
| 139 | + |
| 140 | +```js |
| 141 | +var routes = { |
| 142 | + path: '/', |
| 143 | + component: App, |
| 144 | + childRoutes: [ |
| 145 | + { path: 'about', component: About }, |
| 146 | + { path: 'inbox', component: Inbox }, |
| 147 | + ] |
| 148 | +}; |
| 149 | + |
| 150 | +React.render(<Router routes={routes} />, document.body); |
| 151 | +``` |
| 152 | + |
| 153 | +### Adding more UI |
| 154 | + |
| 155 | +Alright, now we're ready to nest the inbox messages inside the inbox UI. |
| 156 | + |
| 157 | +```js |
| 158 | +// Make a new component to render inside of Inbox |
| 159 | +var Message = React.createClass({ |
| 160 | + render() { |
| 161 | + return <h3>Message</h3>; |
| 162 | + } |
| 163 | +}); |
| 164 | + |
| 165 | +var Inbox = React.createClass({ |
| 166 | + render() { |
| 167 | + return ( |
| 168 | + <div> |
| 169 | + <h2>Inbox</h2> |
| 170 | + {/* Render the child route component */} |
| 171 | + {this.props.children || "Welcome to your Inbox"} |
| 172 | + </div> |
| 173 | + ); |
| 174 | + } |
| 175 | +}); |
| 176 | + |
| 177 | +React.render(( |
| 178 | + <Router> |
| 179 | + <Route path="/" component={App}> |
| 180 | + <Route path="about" component={About}/> |
| 181 | + <Route path="inbox" component={Inbox}> |
| 182 | + {/* Add the route, nested where we want the UI to nest */} |
| 183 | + <Route path="messages/:id" component={Message}/> |
| 184 | + </Route> |
| 185 | + </Route> |
| 186 | + </Router> |
| 187 | +), document.body); |
| 188 | +``` |
| 189 | + |
| 190 | +Now visits to URLs like `inbox/messages/Jkei3c32` will match the new route and nest the UI branch of `App -> Inbox -> Message`. |
| 191 | + |
| 192 | +### Getting URL parameters |
| 193 | + |
| 194 | +We're going to need to know something about the message in order to fetch it from the server. Route components get some useful properties injected into them when you render, particularly the parameters from the dynamic segment of your path. In our case, `:id`. |
| 195 | + |
| 196 | +```js |
| 197 | +var Message = React.createClass({ |
| 198 | + |
| 199 | + componentDidMount() { |
| 200 | + // from the path `/inbox/messages/:id` |
| 201 | + var id = this.props.params.id; |
| 202 | + |
| 203 | + fetchMessage(id, function (err, message) { |
| 204 | + this.setState({ message: message }); |
| 205 | + }); |
| 206 | + }, |
| 207 | + |
| 208 | + // ... |
| 209 | + |
| 210 | +}); |
| 211 | +``` |
| 212 | + |
| 213 | +That's the gist of React Router. Application UIs are boxes inside of boxes inside of boxes; now you can keep those boxes in sync with the URL and link to them easily. |
0 commit comments