- {resources.map(resource => (
+ {Object.keys(resources).map(name => (
@@ -296,7 +297,7 @@ For more details on predefined themes and custom themes, refer to the [Material
## `layout`
-If you want to deeply customize the app header, the menu, or the notifications, the best way is to provide a custom layout component. It must contain a `{children}` placeholder, where react-admin will render the resources. If you use material UI fields and inputs, it should contain a `
` element. And finally, if you want to show the spinner in the app header when the app fetches data in the background, the Layout should connect to the redux store.
+If you want to deeply customize the app header, the menu, or the notifications, the best way is to provide a custom layout component. It must contain a `{children}` placeholder, where react-admin will render the resources.
Use the [default layout](https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/layout/Layout.tsx) as a starting point, and check [the Theming documentation](./Theming.md#using-a-custom-layout) for examples.
diff --git a/docs/Resource.md b/docs/Resource.md
index e932ea9535d..afd9d724a5f 100644
--- a/docs/Resource.md
+++ b/docs/Resource.md
@@ -11,9 +11,9 @@ In react-admin terms, a *resource* is a string that refers to an entity type (li
A `` component has 3 responsibilities:
-- It defines the page components to use for interacting with the resource records (to display a list of records, the details of a record, or to create a new one).
-- It initializes the internal data store so that react-admin components can see it as a mirror of the API for a given resource.
-- It creates a context that lets every descendent component know in which resource they are used (this context is called `ResourceContext`).
+- It defines the components for the CRUD routes of a given resource (to display a list of records, the details of a record, or to create a new one).
+- It creates a context that lets every descendent component know the current resource name (this context is called `ResourceContext`).
+- It stores the resource definition (its name, icon, and label) inside a shared context (this context is called `ResourceDefinitionContext`).
`` components can only be used as children of [the `` component](./Admin.md).
@@ -44,8 +44,6 @@ const App = () => (
);
```
-**Tip**: You must add a `` when you declare a reference (via ``, ``, ``, `` or ``), because react-admin uses resources to define the data store structure. That's why there is an empty `tags` resource in the example above.
-
**Tip**: How does a resource map to an API endpoint? The `` component doesn't know this mapping - it's [the `dataProvider`'s job](./DataProviders.md) to define it.
## `name`
diff --git a/docs/Theming.md b/docs/Theming.md
index bea959ed847..c1a3687273e 100644
--- a/docs/Theming.md
+++ b/docs/Theming.md
@@ -853,7 +853,7 @@ const App = () => (
);
```
-**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations from the Redux store:
+**Tip**: You can generate the menu items for each of the resources by reading the Resource configurations context:
```jsx
// in src/Menu.js
@@ -861,26 +861,26 @@ import * as React from 'react';
import { createElement } from 'react';
import { useSelector } from 'react-redux';
import { useMediaQuery } from '@material-ui/core';
-import { DashboardMenuItem, Menu, MenuItemLink, getResources } from 'react-admin';
+import { DashboardMenuItem, Menu, MenuItemLink, useResourceDefinitions } from 'react-admin';
import DefaultIcon from '@material-ui/icons/ViewList';
import LabelIcon from '@material-ui/icons/Label';
export const Menu = (props) => {
- const resources = useSelector(getResources);
+ const resources = useResourceDefinitions()
const open = useSelector(state => state.admin.ui.sidebarOpen);
return (
,
- { admin: { resources: { posts: { data: {} } } } }
+
);
// waitFor for the dataProvider.getOne() return
await waitFor(() => {
@@ -298,8 +297,7 @@ describe('
', () => {
- ,
- { admin: { resources: { posts: { data: {} } } } }
+
);
// waitFor for the dataProvider.getOne() return
await waitFor(() => {
@@ -352,8 +350,7 @@ describe('
', () => {
- ,
- { admin: { resources: { posts: { data: {} } } } }
+
);
// waitFor for the dataProvider.getOne() return
await waitFor(() => {
diff --git a/packages/ra-ui-materialui/src/detail/Edit.spec.tsx b/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
index bfbfc4d2560..c58170991ec 100644
--- a/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
+++ b/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
@@ -39,8 +39,7 @@ describe('
', () => {
- ,
- { admin: { resources: { foo: { data: {} } } } }
+
);
await waitFor(() => {
expect(queryAllByText('lorem')).toHaveLength(1);
diff --git a/packages/ra-ui-materialui/src/layout/Menu.tsx b/packages/ra-ui-materialui/src/layout/Menu.tsx
index fc4f23dc697..42c522b5b6c 100644
--- a/packages/ra-ui-materialui/src/layout/Menu.tsx
+++ b/packages/ra-ui-materialui/src/layout/Menu.tsx
@@ -1,18 +1,22 @@
import * as React from 'react';
+import { ReactNode, createElement } from 'react';
import { styled } from '@mui/material/styles';
-import { ReactNode } from 'react';
import PropTypes from 'prop-types';
-import { shallowEqual, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
import lodashGet from 'lodash/get';
import DefaultIcon from '@mui/icons-material/ViewList';
import classnames from 'classnames';
-import { useGetResourceLabel, getResources, ReduxState } from 'ra-core';
+import {
+ useResourceDefinitions,
+ useGetResourceLabel,
+ ReduxState,
+} from 'ra-core';
import { DashboardMenuItem } from './DashboardMenuItem';
import { MenuItemLink } from './MenuItemLink';
export const Menu = (props: MenuProps) => {
- const resources = useSelector(getResources, shallowEqual) as Array
;
+ const resources = useResourceDefinitions();
const getResourceLabel = useGetResourceLabel();
const {
hasDashboard,
@@ -20,17 +24,17 @@ export const Menu = (props: MenuProps) => {
children = (
<>
{hasDashboard && }
- {resources
- .filter(r => r.hasList)
- .map(resource => (
+ {Object.keys(resources)
+ .filter(name => resources[name].hasList)
+ .map(name => (
+ resources[name].icon ? (
+ createElement(resources[name].icon)
) : (
)
diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx
index b6294c40d62..4228061e958 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx
@@ -16,18 +16,7 @@ const render = element =>
renderWithRedux(
,
- {
- admin: {
- resources: {
- posts: {
- list: {
- expanded: [],
- },
- },
- },
- },
- }
+
);
describe('', () => {