Skip to content

Commit fdfc40d

Browse files
authored
Merge pull request #9019 from marmelab/list-no-store
Allow disabling store persistence of the list parameters
2 parents 0c30bed + c6fe5d2 commit fdfc40d

8 files changed

Lines changed: 209 additions & 11 deletions

File tree

docs/List.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ You can find more advanced examples of `<List>` usage in the [demos](./Demos.md)
7171
| `queryOptions` | Optional | `object` | - | The options to pass to the `useQuery` hook. |
7272
| `resource` | Optional | `string` | - | The resource name, e.g. `posts`. |
7373
| `sort` | Optional | `object` | - | The initial sort parameters. |
74-
| `storeKey` | Optional | `string` | - | The key to use to store the current filter & sort. |
74+
| `storeKey` | Optional | `string` | `false` | - | The key to use to store the current filter & sort. Pass `false` to disable |
7575
| `title` | Optional | `string` | - | The title to display in the App Bar. |
7676
| `sx` | Optional | `object` | - | The CSS styles to apply to the component. |
7777

@@ -849,6 +849,8 @@ const Admin = () => {
849849

850850
**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.
851851

852+
You can disable this feature by setting the `storeKey` prop to `false`. When disabled, parameters will not be persisted in the store.
853+
852854
## `title`
853855

854856
The default title for a list view is "[resource] list" (e.g. "Posts list"). Use the `title` prop to customize the List view title:

docs/useListController.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ const FlopPosts = (
136136
```
137137
{% endraw %}
138138

139+
You can disable this feature by setting the `storeKey` prop to `false`. When disabled, parameters will not be persisted in the store.
140+
141+
139142
## Return Value
140143

141144
The return value of `useListController` has the following shape:

packages/ra-core/src/controller/list/useInfiniteListController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export interface InfiniteListControllerProps<
218218
>;
219219
resource?: string;
220220
sort?: SortPayload;
221-
storeKey?: string;
221+
storeKey?: string | false;
222222
}
223223

224224
export interface InfiniteListControllerResult<RecordType extends RaRecord = any>

packages/ra-core/src/controller/list/useListController.storeKey.spec.tsx

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import {
77
act,
88
} from '@testing-library/react';
99
import { createMemoryHistory } from 'history';
10-
import { ListsUsingSameResource } from './useListController.storeKey.stories';
10+
import {
11+
ListsUsingSameResource,
12+
ListsWithoutStore,
13+
} from './useListController.storeKey.stories';
1114

1215
describe('useListController', () => {
13-
describe('customStoreKey', () => {
16+
describe('storeKey', () => {
1417
it('should keep distinct two lists of the same resource given different keys', async () => {
1518
render(
1619
<ListsUsingSameResource
@@ -43,5 +46,67 @@ describe('useListController', () => {
4346
screen.getByLabelText('perPage').getAttribute('data-value')
4447
).toEqual('3');
4548
});
49+
50+
it('should not use the store when storeKey is false', async () => {
51+
render(
52+
<ListsWithoutStore
53+
history={createMemoryHistory({
54+
initialEntries: ['/store'],
55+
})}
56+
/>
57+
);
58+
59+
await waitFor(() => {
60+
expect(
61+
screen.getByLabelText('perPage').getAttribute('data-value')
62+
).toEqual('3');
63+
});
64+
65+
act(() => {
66+
fireEvent.click(screen.getByLabelText('incrementPerPage'));
67+
fireEvent.click(screen.getByLabelText('incrementPerPage'));
68+
});
69+
70+
await waitFor(() => {
71+
expect(
72+
screen.getByLabelText('perPage').getAttribute('data-value')
73+
).toEqual('5');
74+
});
75+
76+
act(() => {
77+
fireEvent.click(screen.getByLabelText('nostore'));
78+
});
79+
expect(
80+
screen.getByLabelText('perPage').getAttribute('data-value')
81+
).toEqual('3');
82+
83+
act(() => {
84+
fireEvent.click(screen.getByLabelText('incrementPerPage'));
85+
});
86+
87+
await waitFor(() => {
88+
expect(
89+
screen.getByLabelText('perPage').getAttribute('data-value')
90+
).toEqual('4');
91+
});
92+
93+
act(() => {
94+
fireEvent.click(screen.getByLabelText('store'));
95+
});
96+
// Shouldn't have changed the store list
97+
await waitFor(() => {
98+
expect(
99+
screen.getByLabelText('perPage').getAttribute('data-value')
100+
).toEqual('5');
101+
});
102+
103+
act(() => {
104+
fireEvent.click(screen.getByLabelText('nostore'));
105+
});
106+
// Should have reset its parameters to their default
107+
expect(
108+
screen.getByLabelText('perPage').getAttribute('data-value')
109+
).toEqual('3');
110+
});
46111
});
47112
});

packages/ra-core/src/controller/list/useListController.storeKey.stories.tsx

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const OrderedPostList = ({
4949
storeKey,
5050
sort,
5151
}: {
52-
storeKey: string;
52+
storeKey: string | false;
5353
sort?: SortPayload;
5454
}) => {
5555
const params = useListController({
@@ -117,6 +117,12 @@ const TopPosts = (
117117
const FlopPosts = (
118118
<OrderedPostList storeKey="flop" sort={{ field: 'votes', order: 'ASC' }} />
119119
);
120+
const StorePosts = (
121+
<OrderedPostList storeKey="store" sort={{ field: 'votes', order: 'ASC' }} />
122+
);
123+
const NoStorePosts = (
124+
<OrderedPostList storeKey={false} sort={{ field: 'votes', order: 'ASC' }} />
125+
);
120126

121127
export const ListsUsingSameResource = (argsOrProps, context) => {
122128
const history = context?.history || argsOrProps.history;
@@ -138,3 +144,39 @@ export const ListsUsingSameResource = (argsOrProps, context) => {
138144
</CoreAdminContext>
139145
);
140146
};
147+
148+
const NoStoreLayout = (props: CoreLayoutProps) => {
149+
return (
150+
<div style={styles.mainContainer}>
151+
<Link aria-label="store" to={`/store`}>
152+
Go to Store List
153+
</Link>{' '}
154+
<Link aria-label="nostore" to={`/nostore`}>
155+
Go to No Store List
156+
</Link>
157+
<br />
158+
<br />
159+
{props.children}
160+
</div>
161+
);
162+
};
163+
export const ListsWithoutStore = (argsOrProps, context) => {
164+
const history = context?.history || argsOrProps.history;
165+
return (
166+
<CoreAdminContext
167+
history={history}
168+
store={localStorageStore()}
169+
dataProvider={dataProvider}
170+
>
171+
<CoreAdminUI layout={NoStoreLayout}>
172+
<CustomRoutes>
173+
<Route path="/store" element={StorePosts} />
174+
</CustomRoutes>
175+
<CustomRoutes>
176+
<Route path="/nostore" element={NoStorePosts} />
177+
</CustomRoutes>
178+
<Resource name="posts" />
179+
</CoreAdminUI>
180+
</CoreAdminContext>
181+
);
182+
};

packages/ra-core/src/controller/list/useListController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export interface ListControllerProps<RecordType extends RaRecord = any> {
199199
}> & { meta?: any };
200200
resource?: string;
201201
sort?: SortPayload;
202-
storeKey?: string;
202+
storeKey?: string | false;
203203
}
204204

205205
const defaultSort = {

packages/ra-core/src/controller/list/useListParams.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,25 @@ export const useListParams = ({
8888
const location = useLocation();
8989
const navigate = useNavigate();
9090
const [localParams, setLocalParams] = useState(defaultParams);
91-
const [params, setParams] = useStore(storeKey, defaultParams);
91+
// As we can't conditionally call a hook, if the storeKey is false,
92+
// we'll ignore the params variable later on and won't call setParams either.
93+
const [params, setParams] = useStore(
94+
storeKey || `${resource}.listParams`,
95+
defaultParams
96+
);
9297
const tempParams = useRef<ListParams>();
9398
const isMounted = useIsMounted();
99+
const disableSyncWithStore = storeKey === false;
94100

95101
const requestSignature = [
96102
location.search,
97103
resource,
98104
storeKey,
99-
JSON.stringify(disableSyncWithLocation ? localParams : params),
105+
JSON.stringify(
106+
disableSyncWithLocation || disableSyncWithStore
107+
? localParams
108+
: params
109+
),
100110
JSON.stringify(filterDefaultValues),
101111
JSON.stringify(sort),
102112
perPage,
@@ -111,7 +121,10 @@ export const useListParams = ({
111121
() =>
112122
getQuery({
113123
queryFromLocation,
114-
params: disableSyncWithLocation ? localParams : params,
124+
params:
125+
disableSyncWithLocation || disableSyncWithStore
126+
? localParams
127+
: params,
115128
filterDefaultValues,
116129
sort,
117130
perPage,
@@ -124,7 +137,10 @@ export const useListParams = ({
124137
// store as well so that we don't lose them after a redirection back
125138
// to the list
126139
useEffect(() => {
127-
if (Object.keys(queryFromLocation).length > 0) {
140+
if (
141+
Object.keys(queryFromLocation).length > 0 &&
142+
!disableSyncWithStore
143+
) {
128144
setParams(query);
129145
}
130146
}, [location.search]); // eslint-disable-line
@@ -377,7 +393,7 @@ export interface ListParamsOptions {
377393
perPage?: number;
378394
resource: string;
379395
sort?: SortPayload;
380-
storeKey?: string;
396+
storeKey?: string | false;
381397
}
382398

383399
interface Parameters extends ListParams {

packages/ra-ui-materialui/src/list/List.stories.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,76 @@ export const StoreKey = () => {
390390
);
391391
};
392392

393+
const BooksWithStoreEnabled = () => (
394+
<List
395+
resource="books"
396+
storeKey="booksStore"
397+
sort={{ field: 'year', order: 'DESC' }}
398+
>
399+
<Datagrid>
400+
<TextField source="id" />
401+
<TextField source="title" />
402+
<TextField source="author" />
403+
<TextField source="year" />
404+
</Datagrid>
405+
</List>
406+
);
407+
408+
const BooksWithStoreDisabled = () => (
409+
<List
410+
resource="books"
411+
storeKey={false}
412+
sort={{ field: 'year', order: 'ASC' }}
413+
>
414+
<Datagrid>
415+
<TextField source="id" />
416+
<TextField source="title" />
417+
<TextField source="author" />
418+
<TextField source="year" />
419+
</Datagrid>
420+
</List>
421+
);
422+
423+
const DisabledStoreDashboard = () => (
424+
<>
425+
<Box>
426+
<Button
427+
component={Link}
428+
sx={{ margin: 2 }}
429+
to="/store"
430+
variant="contained"
431+
>
432+
See books with store enabled
433+
</Button>
434+
<Button
435+
component={Link}
436+
sx={{ margin: 2 }}
437+
to="/nostore"
438+
variant="contained"
439+
>
440+
See books with store disabled
441+
</Button>
442+
</Box>
443+
</>
444+
);
445+
446+
export const StoreDisabled = () => {
447+
history.push('/');
448+
return (
449+
<Admin
450+
dataProvider={dataProvider}
451+
history={history}
452+
dashboard={DisabledStoreDashboard}
453+
>
454+
<CustomRoutes>
455+
<Route path="/store" element={<BooksWithStoreEnabled />} />
456+
<Route path="/nostore" element={<BooksWithStoreDisabled />} />
457+
</CustomRoutes>
458+
<Resource name="books" />
459+
</Admin>
460+
);
461+
};
462+
393463
export const ErrorInFetch = () => (
394464
<Admin
395465
dataProvider={

0 commit comments

Comments
 (0)