aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/vendor/react-router/docs/guides/overview.md
blob: 9acd811510c740a88886bea5d78f8c05abc7de17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
React Router Guide
==================

Nesting UI is at the core of React Router. Think about any user
interface you're accustomed to, there is likely some shared UI as you
navigate around the application.

Let's imagine a little app with a dashboard, inbox, and calendar.

```
+---------------------------------------------------------+
| +---------+ +-------+ +--------+                        |
| |Dashboard| | Inbox | |Calendar|      Logged in as Joe  |
| +---------+ +-------+ +--------+                        |
+---------------------------------------------------------+
|                                                         |
|                        Dashboard                        |
|                                                         |
|                                                         |
|   +---------------------+    +----------------------+   |
|   |                     |    |                      |   |
|   | +              +    |    +--------->            |   |
|   | |              |    |    |                      |   |
|   | |   +          |    |    +------------->        |   |
|   | |   |    +     |    |    |                      |   |
|   | |   |    |     |    |    |                      |   |
|   +-+---+----+-----+----+    +----------------------+   |
|                                                         |
+---------------------------------------------------------+
```

We have three main screens here with the top section of UI being
persistent.

Without React Router
--------------------

Without this router, you'd share that UI by repeating render code across
your views, probably with a `<Header/>` component:

```js
var Header = React.createClass({
  render: function() {
    return (
      <header>
        <ul>
          <li><a href="/">Dashboard</a></li>
          <li><a href="/inbox">Inbox</a></li>
          <li><a href="/calendar">Calendar</a></li>
        </ul>
        Logged in as Joe
      </header>
    );
  }
});

var DashboardRoute = React.createClass({
  render: function() {
    return (
      <div>
        <Header/>
        <Dashboard/>
      </div>
    );
  }
});

var InboxRoute = React.createClass({
  render: function() {
    return (
      <div>
        <Header/>
        <Inbox/>
      </div>
    );
  }
});

var CalendarRoute = React.createClass({
  render: function() {
    return (
      <div>
        <Header/>
        <Calendar/>
      </div>
    );
  }
});

// Not React Router API
otherRouter.route('/', function() {
  React.renderComponent(<DashboardRoute/>, document.body);
});

otherRouter.route('/inbox', function() {
  React.renderComponent(<InboxRoute/>, document.body);
});

otherRouter.route('/calendar', function() {
  React.renderComponent(<CalendarRoute/>, document.body);
});

```

The three main view's render methods are nearly identical. While one
level of shared UI like this is pretty easy to handle, getting deeper
and deeper adds more complexity, along with lots of `switch` branching,
etc.

React Router embraces this common pattern among user interfaces by
nesting the views for you. 

With React Router
-----------------

Here's how it works:

1. You declare your view hierarchy with nested `<Route/>`s and provide
   them with a React component to handle the route when its active.

2. React Router will match the deepest route against the URL, and then
   activate the entire tree of routes on that branch, nesting all the
   UI.

3. You access the active route handler in the props of the parent route.

```js
var App = React.createClass({
  render: function() {
    return (
      <div>
        <header>
          <ul>
            <li><Link to="app">Dashboard</Link></li>
            <li><Link to="inbox">Inbox</Link></li>
            <li><Link to="calendar">Calendar</Link></li>
          </ul>
          Logged in as Joe
        </header>

        {/* this is the important part */}
        <this.props.activeRouteHandler/>
      </div>
    );
  }
});

var routes = (
  <Routes location="history">
    <Route name="app" path="/" handler={App}>
      <Route name="inbox" handler={Inbox}/>
      <Route name="calendar" handler={Calendar}/>
      <DefaultRoute handler={Dashboard}/>
    </Route>
  </Routes>
);

React.renderComponent(routes, document.body);
```

When the user lands at `/inbox`, the route named `inbox` gets matched so
its parent route will render the `App` component, and since `inbox` is
active, you get `Inbox` as `this.props.activeRouteHandler`. This is
nearly identical to `{{outlet}}` from Ember or `<div ng-view/>` from
angular.

When the user navigates to `/calendar`, the same thing happens except
now `Calendar` is the `activeRouteHandler` in `App`'s render method.

Finally, when the user navigates to the path `/`, `App` is active, and
notices that it has a `DefaultRoute`, so it receives `Dashboard` as the
`activeRouteHandler`. If a `DefaultRoute` is defined, it will be active
when the parent's route is matched exactly.

Note that we don't need the `<Header/>` component since we don't have to
repeat it anymore. React Router shares that UI for us from one place.

More Nesting
------------

Nesting arbitarily deep UI is not a problem. Consider the `Inbox`
screen: it has a master list of messages on the left, a detail view of
the message on the right, and a toolbar over the top. The toolbar and
list are persistent, meanwhile the message view changes as the user
navigates through the messages.

```
+---------------------------------------------------------------------+
| +---------+ +-------+ +--------+                                    |
| |Dashboard| | Inbox | |Calendar|                   Logged in as Joe |
| +---------+ +-------+ +--------+                                    |
+---------------------------------------------------------------------+
| +---------+ +-------+                              +--------------+ |
| | Compose | | Reply |                              |Inbox Settings| |
| +---------+ +-------+                              +--------------+ |
+-------------------+-------------------------------------------------+
| David Brown       |                                                 |
| Hey, we need to...|                                                 |
|                   |                                                 |
|           12:30pm |                                                 |
+-------------------+                32 Unread Messages               |
| Mary Sweeney      |                                                 |
| I followed up w...|               456 Total Messages                |
|                   |                                                 |
|           12:10pm |                 3 Draft Messages                |
+-------------------+                                                 |
| DeMarcus Jones    |                                                 |
| check this out ...|                                                 |
|                   |                                                 |
|           11:25am |                                                 |
+-------------------+-------------------------------------------------+
```

Let's see how React Router handles this:

```js
var Inbox = React.createClass({
  render: function() {
    return (
      <div>
        <Toolbar/>
        <Messages/>
        <this.props.activeRouteHandler/>
      </div>
    );
  }
});

var routes = (
  <Routes location="history">
    <Route handler={App}>

      <Route name="inbox" handler={Inbox}>
        <Route name="message" path=":messageId" handler={Message}/>
        <DefaultRoute handler={InboxStats}/>
      </Route>

      <Route name="calendar" handler={Calendar}/>
      <DefaultRoute handler={Dashboard}/>

    </Route>
  </Routes>
);
```

- Inbox now has `this.props.activeRouteHandler` in its render method,
  exactly like its parent.
- We added a child routes to `inbox`; messages or the stats page can now
  render into it.

Nesting a new level of UI does not increase the complexity of your code.
You simply nest some routes and render them with `activeRouteHandler`.

Dynamic Segments
----------------

When we added the `message` route, we introduced a "dynamic segment" to
the URL. These segements get parsed from the url and passed into your
route handler on `this.props.params`.

Remember our message route looks like this:

```xml
<Route name="message" path=":messageId" handler={Message}/>
```

Lets look at accessing the `messageId` in `Message`.

```js
var Message = React.createClass({
  render: function() {
    return (
      <div>{this.props.params.messageId}</div>
    );
  }
});
```

Assuming the user navigates to `/inbox/123`, `this.props.params.messageId` is
going to be `'123'`. Check out the [AsyncState][AsyncState] mixin to see
how you can turn this parameter into state on your component. Or for a
more basic approach, make an ajax call in `componentDidMount` with the
value.

Important Note About Dynamic Segments
-------------------------------------

If you have dynamic segments in your URL, a transition from `/users/123`
to `/users/456` does not call `getInitialState`, `componentWillMount` or
`componentWillUnmount`. If you are using those lifecycle hooks to fetch
data and set state, you will also need to implement
`componentWillReceiveProps` on your handler, just like any other
component whose props are changing. This way you can leverage the
performance of the React DOM diff algorithm. Look at the `Contact`
handler in the `master-detail` example.

If you'd rather be lazy, you can use the `addHandlerKey` option and set
it to `true` on your route to opt-out of the performance. See also
[Route][Route].

Scrolling
---------

By default, the router will manage the scroll position between route
transitions. When a user clicks "back" or "forward", it will restore
their scroll position. If they visit a new route, it will automatically
scroll the window to the top. You can opt out of this with the
`preserverScrollPosition` option on [Routes][Routes] or [Route][Route].

Bells and Whistles
------------------

### `<Link/>`

The `<Link/>` component allows you to conveniently navigate users around
the application with accessible anchor tags that don't break normal link
functionality like control/command clicking to open in a new tab. Also,
when the route a link references is active, you get the `active` css
class to easily style your UI.

### `<NotFoundRoute/>`

At any level of your UI nesting, you can render a handler if the url
beyond what was matched isn't recognized.

```xml
<Routes location="history">
  <Route path="/" handler={App}>
    <Route name="inbox" path="/inbox" handler={Inbox}>
      <!--
        will render inside the `Inbox` UI for any paths not recognized
        after the parent route's path `/inbox/*`
      -->
      <NotFoundRoute handler={InboxNotFound}
      <Route name="message" path="/inbox/:messageId" handler={Message}/>
      <DefaultRoute handler={InboxStats}/>
    </Route>
    <Route name="calendar" path="/calendar" handler={Calendar}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
  <!-- will catch any route that isn't recognized at all -->
  <NotFoundRoute handler={NotFound}
</Routes>
```

### `<Redirect/>`

URLs in an app change, so we made it easy to not break the old ones.

```xml
<Route name="message" path="/inbox/:messageId" handler={Message} />
<Redirect path="/messages/:messageId" to="message" />
```

Path Matching
-------------

There's a lot more to be said about path matching, check out the [Path
Matching Guide][path-matching].

API Documentation
-----------------

That's the gist of what this router is all about, but there's a lot more
it has to offer. Check out the [API Docs][API] to learn about
redirecting transitions, query parameters and more.

  [AsyncState]:../api/mixins/AsyncState.md
  [Route]:../api/components/Route.md
  [Routes]:../api/components/Routes.md
  [API]:../api/
  [path-matching]:./path-matching.md