Skip to content

Commit

Permalink
Support for suspend after initial mounting for plural fragments
Browse files Browse the repository at this point in the history
Differential Revision: D47028283

fbshipit-source-id: bf44da48e7fbf155f14e716c92958c75e41e9a38
  • Loading branch information
yczhu authored and facebook-github-bot committed Jul 18, 2023
1 parent f4bdd5a commit b09fd94
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 5 deletions.
26 changes: 21 additions & 5 deletions packages/react-relay/relay-hooks/FragmentResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ type FragmentResourceCache = Cache<
result: FragmentResult,
}
| {kind: 'done', result: FragmentResult}
| {kind: 'missing', result: FragmentResult, snapshot: Snapshot},
| {
kind: 'missing',
result: FragmentResult,
snapshot: SingularOrPluralSnapshot,
},
>;

const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
Expand Down Expand Up @@ -839,10 +843,22 @@ class FragmentResourceImpl {
? [...currentSnapshot]
: [...baseSnapshots];
nextSnapshots[idx] = latestSnapshot;
this._cache.set(cacheKey, {
kind: 'done',
result: getFragmentResult(cacheKey, nextSnapshots, storeEpoch),
});
const result = getFragmentResult(cacheKey, nextSnapshots, storeEpoch);
if (
RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
result.isMissingData
) {
this._cache.set(cacheKey, {
kind: 'missing',
result,
snapshot: nextSnapshots,
});
} else {
this._cache.set(cacheKey, {
kind: 'done',
result,
});
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () =>
let UserQuery;
let ViewerFriendsQuery;
let viewerOperation;
let UsersFragment;
let UsersQuery;
let pluralOperation;

const pluralVariables = {ids: ['user-id-1']};

beforeEach(() => {
RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE = true;
Expand All @@ -63,6 +68,25 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () =>
}
`;

UsersFragment = graphql`
fragment FragmentResourceWithOperationTrackerSuspenseTest2Fragment on User
@relay(plural: true) {
id
name
}
`;

UsersQuery = graphql`
query FragmentResourceWithOperationTrackerSuspenseTest2Query(
$ids: [ID!]!
) {
nodes(ids: $ids) {
__typename
...FragmentResourceWithOperationTrackerSuspenseTest2Fragment
}
}
`;

ViewerFriendsQuery = graphql`
query FragmentResourceWithOperationTrackerSuspenseTestViewerFriendsQuery {
viewer {
Expand All @@ -83,8 +107,12 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () =>
id: 'user-id-1',
});
viewerOperation = createOperationDescriptor(ViewerFriendsQuery, {});
pluralOperation = createOperationDescriptor(UsersQuery, pluralVariables);

environment.execute({operation: viewerOperation}).subscribe({});
environment.execute({operation: nodeOperation}).subscribe({});
environment.execute({operation: pluralOperation}).subscribe({});

environment.subscribe(
environment.lookup(viewerOperation.fragment),
jest.fn(),
Expand All @@ -103,6 +131,17 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () =>
),
jest.fn(),
);
environment.subscribe(
environment.lookup(
createReaderSelector(
UsersFragment,
'user-id-1',
pluralOperation.request.variables,
pluralOperation.request,
),
),
jest.fn(),
);
});

afterEach(() => {
Expand Down Expand Up @@ -200,4 +239,112 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () =>
name: 'Alice222',
});
});

it('should throw promise for plural fragment', () => {
environment.commitPayload(viewerOperation, {
viewer: {
actor: {
id: 'viewer-id',
__typename: 'User',
friends: {
pageInfo: {
hasNextPage: true,
hasPrevPage: false,
startCursor: 'cursor-1',
endCursor: 'cursor-1',
},
edges: [
{
cursor: 'cursor-1',
node: {
id: 'user-id-1',
name: 'Alice',
__typename: 'User',
},
},
],
},
},
},
});

const fragment2Ref = {
__id: 'user-id-1',
__fragments: {
FragmentResourceWithOperationTrackerSuspenseTest2Fragment: {},
},
__fragmentOwner: pluralOperation.request,
};

let result = FragmentResource.read(
getFragment(UsersFragment),
[fragment2Ref],
componentName,
);
FragmentResource.subscribe(result, jest.fn());

const fragmentRef = {
__id: 'user-id-1',
__fragments: {
FragmentResourceWithOperationTrackerSuspenseTestFragment: {},
},
__fragmentOwner: nodeOperation.request,
};

const result2 = FragmentResource.read(
getFragment(UserFragment),
fragmentRef,
componentName,
);
FragmentResource.subscribe(result2, jest.fn());

// Execute the nodeOperation query with executeMutation and set the record as undefined in optimistic updater
environment
.executeMutation({
operation: nodeOperation,
optimisticUpdater: store => {
const record = store.get('user-id-1');
record?.setValue(undefined, 'name');
},
})
.subscribe({});

let thrown = null;
try {
FragmentResource.read(
getFragment(UsersFragment),
[fragment2Ref],
componentName,
);
} catch (p) {
expect(p).toBeInstanceOf(Promise);
thrown = p;
}
expect(thrown).not.toBe(null);

environment.mock.nextValue(nodeOperation, {
data: {
node: {
__typename: 'User',
id: 'user-id-1',
name: 'Alice222',
},
},
});

environment.mock.complete(nodeOperation.request.node);
expect(thrown).resolves.not.toThrow();

result = FragmentResource.read(
getFragment(UsersFragment),
[fragment2Ref],
componentName,
);
expect(result.data).toEqual([
{
id: 'user-id-1',
name: 'Alice222',
},
]);
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b09fd94

Please sign in to comment.