Skip to content

Commit 3873809

Browse files
alunyovfacebook-github-bot
authored andcommitted
Handle missed updates from the low-priority state changes in the new hooks implementation
Reviewed By: josephsavona Differential Revision: D52541289 fbshipit-source-id: 6e2d35814e4b9138cf9c9027e6314fe2cc483109
1 parent aac57b3 commit 3873809

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

packages/react-relay/relay-hooks/__tests__/useFragmentNode-test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,58 @@ describe.each([
11181118
});
11191119
});
11201120

1121+
it('should return the latest data when the hi-priority update happens at the same time as the low-priority store update', () => {
1122+
const startTransition = React.startTransition;
1123+
if (startTransition != null) {
1124+
internalAct(() => {
1125+
renderSingularFragment({
1126+
isConcurrent: true,
1127+
});
1128+
});
1129+
assertFragmentResults([
1130+
{
1131+
data: {
1132+
id: '1',
1133+
name: 'Alice',
1134+
profile_picture: null,
1135+
...createFragmentRef('1', singularQuery),
1136+
},
1137+
},
1138+
]);
1139+
1140+
internalAct(() => {
1141+
// Trigger store update with the lower priority
1142+
startTransition(() => {
1143+
environment.commitUpdate(store => {
1144+
store.get('1')?.setValue('Alice Updated Name', 'name');
1145+
});
1146+
});
1147+
// Trigger a hi-pri update with the higher priority, that should force component to re-render
1148+
forceSingularUpdate();
1149+
});
1150+
1151+
// Assert that the component re-renders twice, both times with the latest data
1152+
assertFragmentResults([
1153+
{
1154+
data: {
1155+
id: '1',
1156+
name: 'Alice Updated Name',
1157+
profile_picture: null,
1158+
...createFragmentRef('1', singularQuery),
1159+
},
1160+
},
1161+
{
1162+
data: {
1163+
id: '1',
1164+
name: 'Alice Updated Name',
1165+
profile_picture: null,
1166+
...createFragmentRef('1', singularQuery),
1167+
},
1168+
},
1169+
]);
1170+
}
1171+
});
1172+
11211173
it('should re-read and resubscribe to fragment when variables change', () => {
11221174
renderSingularFragment();
11231175
assertFragmentResults([

packages/react-relay/relay-hooks/experimental/useFragmentInternal_EXPERIMENTAL.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ function subscribeToSnapshot(
244244
environment: IEnvironment,
245245
state: FragmentState,
246246
setState: StateUpdaterFunction<FragmentState>,
247+
hasPendingStateChanges: {current: boolean},
247248
): () => void {
248249
if (state.kind === 'bailout') {
249250
return () => {};
@@ -264,11 +265,14 @@ function subscribeToSnapshot(
264265
name: 'useFragment.subscription.missedUpdates',
265266
hasDataChanges: dataChanged,
266267
});
268+
hasPendingStateChanges.current = dataChanged;
267269
return dataChanged ? nextState : prevState;
268270
} else {
269271
return prevState;
270272
}
271273
}
274+
275+
hasPendingStateChanges.current = true;
272276
return {
273277
kind: 'singular',
274278
snapshot: latestSnapshot,
@@ -297,13 +301,16 @@ function subscribeToSnapshot(
297301
name: 'useFragment.subscription.missedUpdates',
298302
hasDataChanges: dataChanged,
299303
});
304+
hasPendingStateChanges.current =
305+
hasPendingStateChanges.current || dataChanged;
300306
return dataChanged ? nextState : prevState;
301307
} else {
302308
return prevState;
303309
}
304310
}
305311
const updated = [...prevState.snapshots];
306312
updated[index] = latestSnapshot;
313+
hasPendingStateChanges.current = true;
307314
return {
308315
kind: 'plural',
309316
snapshots: updated,
@@ -529,6 +536,8 @@ function useFragmentInternal_EXPERIMENTAL(
529536
// they're missing even though we are out of options for possibly fetching them:
530537
handlePotentialSnapshotErrorsForState(environment, state);
531538

539+
const hasPendingStateChanges = useRef<boolean>(false);
540+
532541
useEffect(() => {
533542
// Check for updates since the state was rendered
534543
let currentState = subscribedState;
@@ -547,9 +556,26 @@ function useFragmentInternal_EXPERIMENTAL(
547556
}
548557
currentState = updatedState;
549558
}
550-
return subscribeToSnapshot(environment, currentState, setState);
559+
return subscribeToSnapshot(
560+
environment,
561+
currentState,
562+
setState,
563+
hasPendingStateChanges,
564+
);
551565
}, [environment, subscribedState]);
552566

567+
if (hasPendingStateChanges.current) {
568+
const updates = handleMissedUpdates(environment, state);
569+
if (updates != null) {
570+
const [hasStateUpdates, updatedState] = updates;
571+
if (hasStateUpdates) {
572+
setState(updatedState);
573+
state = updatedState;
574+
}
575+
}
576+
hasPendingStateChanges.current = false;
577+
}
578+
553579
let data: ?SelectorData | Array<?SelectorData>;
554580
if (isPlural) {
555581
// Plural fragments require allocating an array of the snapshot data values,

0 commit comments

Comments
 (0)