Skip to content

Commit ef85fc1

Browse files
authoredNov 5, 2020
Merge pull request #5487 from marmelab/Fix-crash-when-navigating-away-during-undo-period
Fix crash when navigating away during undo period
2 parents 0300a6c + 91da4d3 commit ef85fc1

File tree

1 file changed

+32
-3
lines changed

1 file changed

+32
-3
lines changed
 

‎packages/ra-core/src/dataProvider/useDataProvider.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getDataProviderCallArguments } from './getDataProviderCallArguments';
2323
// These calls get replayed once the dataProvider exits optimistic mode
2424
const optimisticCalls = [];
2525
const undoableOptimisticCalls = [];
26+
let nbRemainingOptimisticCalls = 0;
2627

2728
/**
2829
* Hook for getting a dataProvider
@@ -190,7 +191,13 @@ const useDataProvider = (): DataProviderProxy => {
190191
} else {
191192
optimisticCalls.push(params);
192193
}
193-
return Promise.resolve();
194+
nbRemainingOptimisticCalls++;
195+
// Return a Promise that only resolves when the optimistic call was made
196+
// otherwise hooks like useQueryWithStore will return loaded = true
197+
// before the content actually reaches the Redux store.
198+
// But as we can't determine when this particular query was finished,
199+
// the Promise resolves only when *all* optimistic queries are done.
200+
return waitFor(() => nbRemainingOptimisticCalls === 0);
194201
}
195202
return doQuery(params);
196203
};
@@ -201,6 +208,18 @@ const useDataProvider = (): DataProviderProxy => {
201208
return dataProviderProxy;
202209
};
203210

211+
// get a Promise that resolves after a delay in milliseconds
212+
const later = (delay = 100): Promise<void> =>
213+
new Promise(function (resolve) {
214+
setTimeout(resolve, delay);
215+
});
216+
217+
// get a Promise that resolves once a condition is satisfied
218+
const waitFor = (condition: () => boolean): Promise<void> =>
219+
new Promise(resolve =>
220+
condition() ? resolve() : later().then(() => waitFor(condition))
221+
);
222+
204223
const doQuery = ({
205224
type,
206225
payload,
@@ -279,7 +298,7 @@ const performUndoableQuery = ({
279298
dispatch,
280299
logoutIfAccessDenied,
281300
allArguments,
282-
}: QueryFunctionParams) => {
301+
}: QueryFunctionParams): Promise<{}> => {
283302
dispatch(startOptimisticMode());
284303
if (window) {
285304
window.addEventListener('beforeunload', warnBeforeClosingWindow, {
@@ -419,18 +438,28 @@ const replayOptimisticCalls = async () => {
419438
// We only handle all side effects queries if there are no more undoable queries
420439
if (undoableOptimisticCalls.length > 0) {
421440
clone = [...undoableOptimisticCalls];
441+
// remove these calls from the list *before* doing them
442+
// because side effects in the calls can add more calls
443+
// so we don't want to erase these.
422444
undoableOptimisticCalls.splice(0, undoableOptimisticCalls.length);
423445

424446
await Promise.all(
425447
clone.map(params => Promise.resolve(doQuery.call(null, params)))
426448
);
449+
// once the calls are finished, decrease the number of remaining calls
450+
nbRemainingOptimisticCalls -= clone.length;
427451
} else {
428452
clone = [...optimisticCalls];
453+
// remove these calls from the list *before* doing them
454+
// because side effects in the calls can add more calls
455+
// so we don't want to erase these.
429456
optimisticCalls.splice(0, optimisticCalls.length);
430457

431458
await Promise.all(
432459
clone.map(params => Promise.resolve(doQuery.call(null, params)))
433460
);
461+
// once the calls are finished, decrease the number of remaining calls
462+
nbRemainingOptimisticCalls -= clone.length;
434463
}
435464
};
436465

@@ -452,7 +481,7 @@ const performQuery = ({
452481
dispatch,
453482
logoutIfAccessDenied,
454483
allArguments,
455-
}: QueryFunctionParams) => {
484+
}: QueryFunctionParams): Promise<any> => {
456485
dispatch({
457486
type: action,
458487
payload,

0 commit comments

Comments
 (0)
Please sign in to comment.