You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/getting-started/concepts.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,8 @@ order: 5
5
5
6
6
# Main Concepts
7
7
8
+
<docs-warning>This document needs to be updated for 6.4 data APIs</docs-warning>
9
+
8
10
<docs-warning>This document is a deep dive into the core concepts behind routing as implemented in React Router. It's pretty long, so if you're looking for a more practical guide check out our [quick start tutorial][tutorial].</docs-warning>
9
11
10
12
You might be wondering what exactly React Router does. How can it help you build your app? What exactly is a **router**, anyway?
Copy file name to clipboardExpand all lines: docs/getting-started/tutorial.md
+112-3Lines changed: 112 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1812,12 +1812,121 @@ function Favorite({ contact }) {
1812
1812
1813
1813
If you click the button now you should see the star _immediately_ change to the new state. Instead of always rendering the actual data, we check if the fetcher has any `formData` being submitted, if so, we'll use that instead. When the action is done, the `fetcher.formData` will no longer exist and we're back to using the actual data. So even if you write bugs in your optimistic UI code, it'll eventually go back to the correct state 🥹
1814
1814
1815
-
In fact, you can click that button as fast as you want with the choppiest network, React Router will automatically handle all interruptions and race conditions on the revalidation. If you're making a `fetch` call in your action, you can even watch it happen on the network tab, it's pretty cool.
1815
+
## Not Found Data
1816
+
1817
+
What happens if the contact we're trying load doesn't exist?
Our root [`errorElement`][errorelement] is catching this unexpected error as we try to render a `null` contact. Nice the error was properly handled, but we can do better!
1822
+
1823
+
Whenever you have an expected error case in a loader or action–like the data not existing–you can `throw`. The call stack will break, React Router will catch it, and the error path is rendered instead. We won't even try to render a `null` contact.
Instead of hitting a render error with `Cannot read properties of null`, we avoid the component completely and render the error path instead, telling the user something more specific.
1843
+
1844
+
This keeps your happy paths, happy. Your route elements don't need to concern themselves with error and loading states.
1845
+
1846
+
## Pathless Routes
1847
+
1848
+
One last thing. The last error page we saw would be better if it rendered inside the root outlet, instead of the whole page. In fact, every error in all of our child routes would be better in the outlet, then the user has more options than hitting refresh.
We could add the error element to every one of the child routes but, since it's all the same error page, this isn't recommended:
1855
+
1856
+
```jsx filename=src/main.jsx lines=[11,18,25,30]
1857
+
<Route
1858
+
path="/"
1859
+
element={<Root />}
1860
+
loader={rootLoader}
1861
+
action={rootAction}
1862
+
errorElement={<ErrorPage />}
1863
+
>
1864
+
<Route
1865
+
index
1866
+
element={<Index/>}
1867
+
errorElement={<ErrorPage />}
1868
+
/>
1869
+
<Route
1870
+
path="contacts/:contactId"
1871
+
element={<Contact />}
1872
+
loader={contactLoader}
1873
+
action={contactAction}
1874
+
errorElement={<ErrorPage />}
1875
+
/>
1876
+
<Route
1877
+
path="contacts/:contactId/edit"
1878
+
element={<EditContact />}
1879
+
loader={contactLoader}
1880
+
action={editAction}
1881
+
errorElement={<ErrorPage />}
1882
+
/>
1883
+
<Route
1884
+
path="contacts/:contactId/destroy"
1885
+
action={destroyAction}
1886
+
errorElement={<ErrorPage />}
1887
+
/>
1888
+
</Route>
1889
+
```
1890
+
1891
+
There's a cleaner way. Routes can be used _without_ a path, which lets them participate in the UI layout without requiring new path segments in the URL. Check it out:
1892
+
1893
+
👉 **Wrap the child routes in a pathless route**
1894
+
1895
+
```jsx filename=src/main.jsx lines=[8,26]
1896
+
<Route
1897
+
path="/"
1898
+
element={<Root />}
1899
+
loader={rootLoader}
1900
+
action={rootAction}
1901
+
errorElement={<ErrorPage />}
1902
+
>
1903
+
<Route errorElement={<ErrorPage />}>
1904
+
<Route index element={<Index/>} />
1905
+
<Route
1906
+
path="contacts/:contactId"
1907
+
element={<Contact />}
1908
+
loader={contactLoader}
1909
+
action={contactAction}
1910
+
/>
1911
+
<Route
1912
+
path="contacts/:contactId/edit"
1913
+
element={<EditContact />}
1914
+
loader={contactLoader}
1915
+
action={editAction}
1916
+
/>
1917
+
<Route
1918
+
path="contacts/:contactId/destroy"
1919
+
action={destroyAction}
1920
+
/>
1921
+
</Route>
1922
+
</Route>
1923
+
```
1924
+
1925
+
When any errors are thrown in the child routes, our new pathless route will catch it and render, preserving the root route's UI!
1816
1926
1817
1927
---
1818
1928
1819
-
- 404
1820
-
- replace delete
1929
+
That's it! Thanks for giving React Router a shot. We hope this tutorial gives you a solid start to build great user experiences. There's a lot more you can do with React Router, so make sure to check out all the APIs 😀
0 commit comments