diff --git a/docs/List.md b/docs/List.md
index 887942956d1..3e657699c95 100644
--- a/docs/List.md
+++ b/docs/List.md
@@ -486,7 +486,7 @@ const Dashboard = () => (
)
```
-Please note that the selection state is not synced in the URL but in a global store using the resource as key. Thus, all lists in the page using the same resource will share the same synced selection state. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. If you want to allow custom `storeKey`'s for managing selection state, you will have to implement your own `useListController` hook and pass a custom key to the `useRecordSelection` hook. You will then need to implement your own `DeleteButton` and `BulkDeleteButton` to manually unselect rows when deleting records. You can still opt out of all store interactions including selection if you set it to `false`.
+Please note that the selection state is not synced in the URL but in a global store using the resource and, if provided, `storeKey` as part of the key. Thus, all lists in the page using the same resource and `storeKey` will share the same synced selection state. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. You can still opt out of all store interactions for list state if you set it to `false`.
## `empty`
@@ -1097,7 +1097,9 @@ const Admin = () => {
**Tip:** The `storeKey` is actually passed to the underlying `useListController` hook, which you can use directly for more complex scenarios. See the [`useListController` doc](./useListController.md#storekey) for more info.
-**Note:** *Selection state* will remain linked to a resource-based key regardless of the specified `storeKey` string. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. If you want to allow custom `storeKey`'s for managing selection state, you will have to implement your own `useListController` hook and pass a custom key to the `useRecordSelection` hook. You will then need to implement your own `DeleteButton` and `BulkDeleteButton` to manually unselect rows when deleting records. You can still opt out of all store interactions including selection if you set it to `false`.
+**Tip:** The `storeKey` is also passed to the underlying `useRecordSelection` hook, so that lists with different storeKeys for same resource will have independent selection states.
+
+**Tip:** Setting `storeKey` to `false` will opt out of all store interactions including selection.
## `title`
diff --git a/packages/ra-core/src/controller/button/useBulkDeleteController.ts b/packages/ra-core/src/controller/button/useBulkDeleteController.ts
index b21465114a5..cb742b57157 100644
--- a/packages/ra-core/src/controller/button/useBulkDeleteController.ts
+++ b/packages/ra-core/src/controller/button/useBulkDeleteController.ts
@@ -47,7 +47,7 @@ export const useBulkDeleteController = <
undoable: mutationMode === 'undoable',
}
);
- onUnselectItems();
+ onUnselectItems(true);
},
onError: (error: any) => {
notify(
diff --git a/packages/ra-core/src/controller/button/useDeleteController.tsx b/packages/ra-core/src/controller/button/useDeleteController.tsx
index 11e6c8f4841..b7723ce85c3 100644
--- a/packages/ra-core/src/controller/button/useDeleteController.tsx
+++ b/packages/ra-core/src/controller/button/useDeleteController.tsx
@@ -94,7 +94,7 @@ export const useDeleteController = <
undoable: mutationMode === 'undoable',
}
);
- record && unselect([record.id]);
+ record && unselect([record.id], true);
redirect(redirectTo, resource);
},
onError: (error: any) => {
diff --git a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.spec.tsx b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.spec.tsx
index be5e46d2423..4a98e4303e0 100644
--- a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.spec.tsx
+++ b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.spec.tsx
@@ -11,6 +11,7 @@ import useDeleteWithConfirmController, {
import { TestMemoryRouter } from '../../routing';
import { useNotificationContext } from '../../notification';
+import { memoryStore, StoreSetter } from '../../store';
describe('useDeleteWithConfirmController', () => {
it('should call the dataProvider.delete() function with the meta param', async () => {
@@ -101,4 +102,51 @@ describe('useDeleteWithConfirmController', () => {
]);
});
});
+
+ it('should unselect records from all storeKeys in useRecordSelection', async () => {
+ const dataProvider = testDataProvider({
+ delete: jest.fn((resource, params) => {
+ return Promise.resolve({ data: params.previousData });
+ }),
+ });
+
+ const MockComponent = () => {
+ const { handleDelete } = useDeleteWithConfirmController({
+ record: { id: 456 },
+ resource: 'posts',
+ mutationMode: 'pessimistic',
+ } as UseDeleteWithConfirmControllerParams);
+ return ;
+ };
+
+ const store = memoryStore();
+
+ render(
+
+
+
+
+ } />
+
+
+
+
+ );
+
+ const button = await screen.findByText('Delete');
+ fireEvent.click(button);
+ await waitFor(
+ () =>
+ expect(store.getItem('posts.selectedIds')).toEqual({
+ ['']: [123],
+ ['bar']: [],
+ }),
+ {
+ timeout: 1000,
+ }
+ );
+ });
});
diff --git a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx
index c45b70818ec..dc54fa46a2f 100644
--- a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx
+++ b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx
@@ -109,7 +109,7 @@ const useDeleteWithConfirmController = <
undoable: mutationMode === 'undoable',
}
);
- record && unselect([record.id]);
+ record && unselect([record.id], true);
redirect(redirectTo, resource);
},
onError: error => {
diff --git a/packages/ra-core/src/controller/field/useReferenceManyFieldController.spec.tsx b/packages/ra-core/src/controller/field/useReferenceManyFieldController.spec.tsx
index 3537e2307b2..f9f05e6f538 100644
--- a/packages/ra-core/src/controller/field/useReferenceManyFieldController.spec.tsx
+++ b/packages/ra-core/src/controller/field/useReferenceManyFieldController.spec.tsx
@@ -332,6 +332,42 @@ describe('useReferenceManyFieldController', () => {
});
});
+ it('should store selection state linked to referencing record', async () => {
+ const store = memoryStore();
+ const setStore = jest.spyOn(store, 'setItem');
+
+ render(
+
+
+ {({ onToggleItem }) => {
+ return (
+
+ );
+ }}
+
+
+ );
+
+ fireEvent.click(await screen.findByText('Toggle'));
+ await waitFor(() => {
+ expect(setStore).toHaveBeenCalledWith('books.selectedIds', {
+ ['authors.123']: [456],
+ });
+ });
+ });
+
it('should support custom storeKey', async () => {
const store = memoryStore();
const setStore = jest.spyOn(store, 'setItem');
@@ -352,7 +388,7 @@ describe('useReferenceManyFieldController', () => {
>
{({ onToggleItem }) => {
return (
-