Skip to content

Commit

Permalink
Merge pull request #5487 from marmelab/Fix-crash-when-navigating-away…
Browse files Browse the repository at this point in the history
…-during-undo-period

Fix crash when navigating away during undo period
  • Loading branch information
djhi authored Nov 5, 2020
2 parents 0300a6c + 91da4d3 commit ef85fc1
Showing 1 changed file with 32 additions and 3 deletions.
35 changes: 32 additions & 3 deletions packages/ra-core/src/dataProvider/useDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getDataProviderCallArguments } from './getDataProviderCallArguments';
// These calls get replayed once the dataProvider exits optimistic mode
const optimisticCalls = [];
const undoableOptimisticCalls = [];
let nbRemainingOptimisticCalls = 0;

/**
* Hook for getting a dataProvider
Expand Down Expand Up @@ -190,7 +191,13 @@ const useDataProvider = (): DataProviderProxy => {
} else {
optimisticCalls.push(params);
}
return Promise.resolve();
nbRemainingOptimisticCalls++;
// Return a Promise that only resolves when the optimistic call was made
// otherwise hooks like useQueryWithStore will return loaded = true
// before the content actually reaches the Redux store.
// But as we can't determine when this particular query was finished,
// the Promise resolves only when *all* optimistic queries are done.
return waitFor(() => nbRemainingOptimisticCalls === 0);
}
return doQuery(params);
};
Expand All @@ -201,6 +208,18 @@ const useDataProvider = (): DataProviderProxy => {
return dataProviderProxy;
};

// get a Promise that resolves after a delay in milliseconds
const later = (delay = 100): Promise<void> =>
new Promise(function (resolve) {
setTimeout(resolve, delay);
});

// get a Promise that resolves once a condition is satisfied
const waitFor = (condition: () => boolean): Promise<void> =>
new Promise(resolve =>
condition() ? resolve() : later().then(() => waitFor(condition))
);

const doQuery = ({
type,
payload,
Expand Down Expand Up @@ -279,7 +298,7 @@ const performUndoableQuery = ({
dispatch,
logoutIfAccessDenied,
allArguments,
}: QueryFunctionParams) => {
}: QueryFunctionParams): Promise<{}> => {
dispatch(startOptimisticMode());
if (window) {
window.addEventListener('beforeunload', warnBeforeClosingWindow, {
Expand Down Expand Up @@ -419,18 +438,28 @@ const replayOptimisticCalls = async () => {
// We only handle all side effects queries if there are no more undoable queries
if (undoableOptimisticCalls.length > 0) {
clone = [...undoableOptimisticCalls];
// remove these calls from the list *before* doing them
// because side effects in the calls can add more calls
// so we don't want to erase these.
undoableOptimisticCalls.splice(0, undoableOptimisticCalls.length);

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

await Promise.all(
clone.map(params => Promise.resolve(doQuery.call(null, params)))
);
// once the calls are finished, decrease the number of remaining calls
nbRemainingOptimisticCalls -= clone.length;
}
};

Expand All @@ -452,7 +481,7 @@ const performQuery = ({
dispatch,
logoutIfAccessDenied,
allArguments,
}: QueryFunctionParams) => {
}: QueryFunctionParams): Promise<any> => {
dispatch({
type: action,
payload,
Expand Down

0 comments on commit ef85fc1

Please sign in to comment.