Skip to content

Commit f6df25d

Browse files
committed
Complete the chapter
1 parent 7790ab5 commit f6df25d

File tree

3 files changed

+130
-39
lines changed

3 files changed

+130
-39
lines changed

docs/Architecture.md

Lines changed: 130 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ title: "Key Concepts"
55

66
# Key Concepts
77

8-
React-admin relies on a few design decisions that structure its codebase.
8+
React-admin relies on a several design decisions that structure its codebase.
99

1010
## Single-Page Application
1111

12-
React-admin is designed to build Single-Page Applications (SPA). It means that in a react-admin app, the browser fetches the HTML, CSS, and JavaScript required to render the application once, and then only fetches data from APIs through AJAX calls. This is in contrast to traditional web applications, where the browser fetches a new HTML page for each screen.
12+
React-admin is specifically designed to build [Single-Page Applications (SPA)](https://en.wikipedia.org/wiki/Single-page_application). In a react-admin app, the browser fetches the required HTML, CSS, and JavaScript to render the application only once. Subsequently, data is fetched from APIs through AJAX calls. This is in contrast to traditional web applications, where the browser fetches a new HTML page for each screen.
1313

14-
The SPA architecture makes react-admin apps [ultra fast](./Features.md#fast), and allows them to work with existing APIs.
14+
![SPA lifecycle](./img/SPA-lifecycle.png)
1515

16-
This implies that react-admin uses an internal router (powered by `react-router`) to display the correct screen when the user clicks on a link. To declare routes, developers use the [`<Resource>`](./Resource.md) component for CRUD routes, and the [`<CustomRoutes>`](./CustomRoutes.md) component for other routes.
16+
The SPA architecture ensures that react-admin apps are [exceptionally fast](./Features.md#fast), easy to host, and compatible with existing APIs without requiring a dedicated backend.
1717

18-
For instance, the following react-admin application:
18+
To achieve this, react-admin utilizes an internal router, powered by `react-router`, to display the appropriate screen when the user clicks on a link. Developers can define routes using the [`<Resource>`](./Resource.md) component for CRUD routes and the [`<CustomRoutes>`](./CustomRoutes.md) component for other routes.
19+
20+
For example, the following react-admin application:
1921

2022
```jsx
2123
import { Admin, Resource, CustomRoutes } from 'react-admin';
@@ -51,7 +53,7 @@ Declares the following routes:
5153
- `/profile`: `<Profile>`
5254
- `/organization`: `<Organization>`
5355

54-
Using the `<Resource>` component for CRUD routes allows react-admin to automatically link CRUD pages between them, including for related entities. It lets you think about your application in terms of entities, and not in terms of routes.
56+
The `<Resource>` component allows react-admin to automatically link CRUD pages between them, including those for related entities. This approach allows you to think about your application in terms of entities, rather than getting bogged down by managing routes.
5557

5658
## Providers
5759

@@ -81,7 +83,7 @@ dataProvider.getList('posts', {
8183
// }
8284
```
8385

84-
How the `getList()` method translates to an HTTP request is up to the data provider. For instance, when using the REST data provider, the above code will translate to:
86+
How the `dataProvider.getList()` method translates to an HTTP request is up to the data provider. For instance, when using the REST data provider, the above code will translate to:
8587

8688
```
8789
GET http://path.to.my.api/posts?sort=["title","ASC"]&range=[0, 4]&filter={"author_id":12}
@@ -137,7 +139,7 @@ React-admin was built to avoid rewriting the same code and over again, because m
137139

138140
<a href="./img/components.webp"><img class="no-shadow" src="./img/components.webp" alt="Smart components" /></a>
139141

140-
React-admin isn't a UI Kit like Material UI or Bootstrap. It's a framework that goes beyond presentation to provide building blocks for data-driven applications. It is built on top of material-ui, but you don't need to know material-ui to start using react-admin.
142+
React-admin isn't a UI Kit like Material UI or Bootstrap. It's a framework that goes beyond presentation to provide building blocks for data-driven applications. It is built on top of Material UI, but you don't need to know Material UI to start using react-admin.
141143

142144
For instance, to write a custom menu for your application, you will use the `<Menu>` component:
143145

@@ -182,19 +184,18 @@ For instance, you cannot pass a list of actions to the `<Edit>` view, but you ca
182184
import { Button } from '@mui/material';
183185
import { TopToolbar, ShowButton } from 'react-admin';
184186

187+
export const PostEdit = () => (
188+
<Edit actions={<PostEditActions />}>
189+
...
190+
</Edit>
191+
);
192+
185193
const PostEditActions = () => (
186194
<TopToolbar>
187195
<ShowButton />
188-
{/* Add your custom actions */}
189196
<Button color="primary" onClick={customAction}>Custom Action</Button>
190197
</TopToolbar>
191198
);
192-
193-
export const PostEdit = () => (
194-
<Edit actions={<PostEditActions />}>
195-
...
196-
</Edit>
197-
);
198199
```
199200

200201
This allows overriding parts of the logic of a component by composing it with another component.
@@ -274,7 +275,7 @@ React-admin exposes [dozens of hooks](./Reference.md#hooks) to help you build yo
274275

275276
## Context: Pull, Don't Push
276277

277-
Communicating between components is a common problem in React applications, especially in large ones, when you have to pass props down several levels. React-admin solves this problem by using a pull model: components expose props to their descendants via a context, and descendants can use them via a custom hook.
278+
Communicating between components is a common problem in React applications. This is especially true in large applications, where you have to pass props down several levels. React-admin solves this problem by using a pull model: components expose props to their descendants via a context, and descendants can use them via a custom hook.
278279

279280
Whenever a react-admin component fetches data or defines a callback, the component creates a context and puts the data and callback in it.
280281

@@ -324,20 +325,96 @@ const StoreShowPage = () => (
324325

325326
This simple approach removes the need for a dependency injection system.
326327

327-
So when you write a component that need to access data or callbacks defined higher in the render tree, you can always find a context to get it.
328+
So when you write a component that needs to access data or callbacks defined higher in the render tree, you can always find a context to get it.
328329

329330
Contexts are one of the key concepts in React Admin. If you are not familiar with them, do not hesitate to read the [React documentation on Context](https://react.dev/learn/passing-data-deeply-with-context).
330331

332+
## Batteries Included But Removable
333+
334+
You can build very complex web apps with react-admin components alone, as long as the react-admin design choices fit yours. But if you need to customize the behavior of a component beyond its existing capabilities, you can always replace a react-admin component with your own.
335+
336+
For instance, if [`<SimpleShowLayout>`](./SimpleShowLayout.md) doesn't let you lay out the details of a contact in the following way:
337+
338+
![contact details](./img/atomic-crm.png)
339+
340+
You can always use your own layout component:
341+
342+
{% raw %}
343+
```tsx
344+
export const ContactShow = () => (
345+
<ShowBase>
346+
<ContactShowContent />
347+
</ShowBase>
348+
);
349+
350+
const ContactShowContent = () => {
351+
const { record, isLoading } = useShowContext<Contact>();
352+
if (isLoading || !record) return null;
353+
return (
354+
<Box mt={2} display="flex">
355+
<Box flex="1">
356+
<Card>
357+
<CardContent>
358+
<Box display="flex">
359+
<Avatar />
360+
<Box ml={2} flex="1">
361+
<Typography variant="h5">
362+
{record.first_name} {record.last_name}
363+
</Typography>
364+
<Typography variant="body2">
365+
{record.title} at{' '}
366+
<ReferenceField
367+
source="company_id"
368+
reference="companies"
369+
link="show"
370+
>
371+
<TextField source="name" />
372+
</ReferenceField>
373+
</Typography>
374+
</Box>
375+
<Box>
376+
<ReferenceField
377+
source="company_id"
378+
reference="companies"
379+
link="show"
380+
>
381+
<LogoField />
382+
</ReferenceField>
383+
</Box>
384+
</Box>
385+
<ReferenceManyField
386+
target="contact_id"
387+
reference="contactNotes"
388+
sort={{ field: 'date', order: 'DESC' }}
389+
>
390+
<NotesIterator showStatus reference="contacts" />
391+
</ReferenceManyField>
392+
</CardContent>
393+
</Card>
394+
</Box>
395+
<ContactAside />
396+
</Box>
397+
);
398+
};
399+
```
400+
{% endraw %}
401+
402+
This example comes from [Atomic CRM](https://marmelab.com/react-admin-crm/#/contacts), one of the react-admin demo applications.
403+
404+
Never hesitate to replace a react-admin component with your own. React-admin cannot cover all use cases, it provides hooks to plug in your own components, and "It's just React"™.
405+
406+
React-admin will never lock you in a corner.
407+
331408
## User Experience Is King
332409

333410
React-admin has two sets of users:
334411

335412
- End users, who use the react-admin app in their browser
336-
- Developers, who build the react-admin app in their IDE
413+
- Developers, who use the react-admin code in their IDE
337414

338415
For each feature, we design the User Experience (UX) and the Developer Experience (DX) carefully.
339416

340-
For the visual part, react-admin builds upon Material UI, which is the implementation of the Material Design System. It's a great help to build usable, consistent user interfaces, but it's not enough.
417+
For the visual part, react-admin builds upon Material UI, which is the implementation of [Material Design](https://m3.material.io/), a carefully crafted design system for web and mobile apps. It's a great help to build usable, consistent user interfaces, but it's not enough.
341418

342419
We spend a great deal of time refining the UI to make it as intuitive as possible. We pay attention to small alignment glitches, screen flashes, and color inconsistencies. We iterate with every customer feedback, to remove visual and animation problems that occur in real-life applications.
343420

@@ -358,15 +435,15 @@ Many excellent open-source libraries already address partial requirements of B2B
358435

359436
Rather than reinventing the wheel, react-admin uses the best tools in each category (in terms of features, developer experience, active maintenance, documentation, user base), and provides a glue around these libraries.
360437

361-
In react-admin v4, these libraries are called react-query, react-router, react-hook-form, Material UI, testing-library, date-fns, and lodash.
438+
In react-admin v4, these libraries are called [react-query](https://tanstack.com/query/v3), [react-router](https://reactrouter.com/en/main), [react-hook-form](https://react-hook-form.com/), [Material UI](https://mui.com/), [emotion](https://emotion.sh/docs/introduction), [testing-library](https://testing-library.com/docs/react-testing-library/intro), [date-fns](https://date-fns.org/), and [lodash](https://lodash.com/).
362439

363440
When a new requirement arises, the react-admin teams always looks for an existing solution, and prefers integrating it rather than redeveloping it.
364441

365-
There is one constraint, though: all react-admin's dependencies must be compatible with the MIT licence.
442+
There is one constraint, though: all react-admin's dependencies must be compatible with the [MIT license](https://github.com/marmelab/react-admin/blob/master/LICENSE.md).
366443

367444
## Minimal API Surface
368445

369-
Before adding a new hook or a new prop to an existing component, we always check if there isn't a simple way to implement the feature in pure React. If it's the case, then we don't add the new prop. We prefer to keep the react-admin API, code, test, and documentation simple. This choice is crucial to keep the learning curve acceptable, and maintenance burden low.
446+
Before adding a new hook or a new prop to an existing component, we always check if there isn't a simple way to implement the feature in pure React. If it's the case, then we don't add the new prop. We prefer to keep the react-admin API, code, test, and documentation simple. This choice is crucial to keep the learning curve acceptable, and the maintenance burden low.
370447

371448
For instance, the `<SimpleShowLayout>` component displays Field elements in a column. How can you put two fields in a single column? We could add a specific syntax allowing to specify the number of elements per column and per line. This would complicate the usage and documentation for simple use cases. Besides, it's doable in pure React, without any change in the react-admin core, e.g. by leveraging Material UI's `<Stack>` component:
372449

@@ -402,41 +479,55 @@ Some components may have a weird API. That's probably for historical reasons. We
402479

403480
The code of some components may seem convoluted for no apparent reason. It's probably that the component has to support both the old and the new syntax.
404481

405-
This backward compatibility costs a lot in maintenance, and we try to reduce this cost by a good automated test coverage.
482+
This backward compatibility costs a lot in maintenance to the react-admin core team, but it's a huge time saver for react-admin users.
483+
484+
## Principle of Least Surprise
485+
486+
Because we favor [composition](#composition), you should be able to combine react-admin components in all sorts of ways, and it should just work (thanks to [contexts](#context-pull-dont-push)). We have an extensive test suite to ensure that the react-admin components work well together. And TypeScript helps you determine when you are using a component in a way that doesn't make sense.
487+
488+
This leads to strong design choices in the react-admin code.
489+
490+
One of them concerns child inspection, which we tend to avoid at all costs. A counter example is `<Datagrid>`, which inspects its Field children at runtime to determine the column headers. This has serious drawbacks:
491+
492+
- If the child is wrapped inside another component that doesn't use the same API, the feature breaks
493+
- Developers expect that a component affects its subtree, not its ancestors. This leads to inexplicable bugs.
494+
495+
We keep child inspection in `<Datagrid>` because there is no better alternative, but it's a rare exception. Every time we implemented child inspection, we regretted it afterward.
496+
497+
To avoid surprises, we also avoid `React.cloneElement()`, and passing props down the tree.
406498

407499
## Principle Of Least Documentation
408500

409501
No one reads docs. It's an unfortunate fact that we have learned to live with.
410502

411-
So when we design a new feature, we try to do it in the most intuitive way for developers. We keep the API minimal (see above). We copy the API of well-known libraries. We throw errors with helpful and explicit messages. We provide TypeScript types and JSDoc to help developers discover the API from within their IDE. We publish live examples with commented code.
503+
So when we design a new feature, we try to do it in the most intuitive way for developers. We keep the API minimal ([see above](#minimal-api-surface)). We copy the API of well-known libraries. We throw errors with helpful and explicit messages. We provide TypeScript types and JSDoc to help developers discover the API from within their IDE. We publish live examples with commented code.
412504

413-
When we have to write documentation, it should contain:
505+
But because react-admin is a very large library, it also has a lengthy documentation. We cover many, many use cases, and our documentation goes beyond basic usage instructions and API description. And to be sure you find the right information quickly, we often duplicate the same information in several places. We believe in the power of [serendipity](https://en.wikipedia.org/wiki/Serendipity).
414506

415-
1. images/screencasts
416-
2. code samples
417-
3. text
507+
Don't be afraid if this documentation seems overwhelming at first. You don't need to read it all. You can start with the Introduction chapter of each section, and read the code of the demos. After a while, you'll get used to the react-admin API, and you'll be able to find the information you need quickly.
418508

419-
In that order of importance.
509+
## Monorepo
420510

421-
## Inspecting Children Is Bad
511+
Whenever you import a react-admin component, it's from the `react-admin` package:
422512

423-
Some components use child inspection for some features. For instance, the `<Datagrid>` inspects its Field children at runtime to determine the column headers. This has serious drawbacks:
513+
```jsx
514+
import { List, Datagrid, TextField } from 'react-admin';
515+
```
424516

425-
- If the child is wrapped inside another component that doesn't use the same API, the feature breaks
426-
- Developers expect that a component affects its subtree, not its ancestors. This leads to inexplicable bugs.
517+
But if you look at [the react-admin source code](https://github.com/marmelab/react-admin) (which we encourage you to do), you will find imports like:
427518

428-
Every time we implemented child inspection, we regretted it afterward. We tend to avoid it at all costs, as well as using `React.cloneElement()`.
519+
```jsx
520+
import { useListController } from 'ra-core';
521+
```
429522

430-
## Monorepo
431-
432-
React-admin is a *distribution* of several packages, each of which handles a specific feature. The packages are all located in the `packages/` directory. The most notable packages are:
523+
In fact, the `react-admin` package only re-exports components from internal packages. React-admin is a *distribution* of several packages, each of which handles a specific feature. The packages are all located in [the `packages/` directory](https://github.com/marmelab/react-admin/tree/master/packages). The most notable packages are:
433524

434525
* `ra-core`: The core react-admin logic, without any UI.
435526
* `ra-ui-materialui`: The Material UI skin for react-admin.
436527
* `ra-data-*`: Data providers for various data backends.
437528
* `ra-language-*`: Interface translations for various languages.
438529
* `react-admin`: the standard distribution of react-admin
439-
440-
You can build your own distribution of react-admin by combining different packages.
530+
531+
You can build your own distribution of react-admin by combining different packages. You can also import hooks and components directly from one of these packages, if you don't want to import the whole react-admin distribution.
441532

442533

docs/img/SPA-lifecycle.png

71.4 KB
Loading

docs/img/atomic-crm.png

494 KB
Loading

0 commit comments

Comments
 (0)