Skip to content

Commit

Permalink
Merge branch 'main' into pr/lint-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas authored Jun 12, 2024
2 parents 694452f + 08dbc02 commit 9eaa883
Show file tree
Hide file tree
Showing 20 changed files with 152 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .api-reports/api-report-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -2145,7 +2145,7 @@ export function useLoadableQuery<TData = unknown, TVariables extends OperationVa
export type UseLoadableQueryResult<TData = unknown, TVariables extends OperationVariables = OperationVariables> = [
loadQuery: LoadQueryFunction<TVariables>,
queryRef: QueryRef<TData, TVariables> | null,
{
handlers: {
fetchMore: FetchMoreFunction<TData, TVariables>;
refetch: RefetchFunction<TData, TVariables>;
reset: ResetFunction;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -1973,7 +1973,7 @@ export function useLoadableQuery<TData = unknown, TVariables extends OperationVa
export type UseLoadableQueryResult<TData = unknown, TVariables extends OperationVariables = OperationVariables> = [
loadQuery: LoadQueryFunction<TVariables>,
queryRef: QueryRef<TData, TVariables> | null,
{
handlers: {
fetchMore: FetchMoreFunction<TData, TVariables>;
refetch: RefetchFunction<TData, TVariables>;
reset: ResetFunction;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -2808,7 +2808,7 @@ export function useLoadableQuery<TData = unknown, TVariables extends OperationVa
export type UseLoadableQueryResult<TData = unknown, TVariables extends OperationVariables = OperationVariables> = [
loadQuery: LoadQueryFunction<TVariables>,
queryRef: QueryRef<TData, TVariables> | null,
{
handlers: {
fetchMore: FetchMoreFunction<TData, TVariables>;
refetch: RefetchFunction<TData, TVariables>;
reset: ResetFunction;
Expand Down
5 changes: 5 additions & 0 deletions .changeset/sharp-cats-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Add missing name to tuple member (fix TS5084)
18 changes: 13 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ jobs:
Tests:
docker:
- image: cimg/node:22.2.0
parameters:
project:
type: string
steps:
- checkout
- run: npm run ci:precheck
- run: npm version
- run: npm ci
- run: if test "<< parameters.project >>" = "Core Tests"; then npm run test:memory; fi
- run:
name: Jest suite with coverage
command: npm run test:ci
command: npm run test:ci -- --selectProjects "<< parameters.project >>"
environment:
JEST_JUNIT_OUTPUT_FILE: "reports/junit/js-test-results.xml"
JEST_JUNIT_OUTPUT_FILE: "reports/junit/js-test-results-<< parameters.project >>.xml"
- store_test_results:
path: reports/junit
- store_artifacts:
Expand All @@ -69,7 +73,7 @@ jobs:
react:
type: string
docker:
- image: cimg/node:22.2.0
- image: cimg/node:22.2.0-browsers
steps:
- checkout
- attach_workspace:
Expand Down Expand Up @@ -124,7 +128,11 @@ workflows:
Build and Test:
jobs:
# - Filesize
- Tests
- Tests:
matrix:
parameters:
project:
["Core Tests", "ReactDOM 17", "ReactDOM 18", "ReactDOM 19"]
- Formatting
- Lint
- BuildTarball
Expand Down Expand Up @@ -165,7 +173,7 @@ workflows:
- "@types/[email protected] @types/[email protected]"
- "@types/react@17 @types/react-dom@17"
- "@types/react@18 @types/react-dom@18"
- "@types/react@npm:[email protected]alpha.3 @types/react-dom@npm:[email protected]alpha.3"
- "@types/react@npm:[email protected]rc.0 @types/react-dom@npm:[email protected]rc.0"
- "typescript@next"
security-scans:
jobs:
Expand Down
6 changes: 4 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 🔮 Apollo Client Roadmap

**Last updated: 2024-04-29**
**Last updated: 2024-06-03**

For up to date release notes, refer to the project's [Changelog](https://github.com/apollographql/apollo-client/blob/main/CHANGELOG.md).

Expand All @@ -16,11 +16,13 @@ For up to date release notes, refer to the project's [Changelog](https://github.
## [3.11.0](https://github.com/apollographql/apollo-client/milestone/40) - July 9th, 2024
_Release candidate - July 2nd, 2024_

- Rewriting `useQuery` and `useSubscription` for better React Compiler support

## Upcoming features

- Data masking
- Introduce a suspenseful `useFragment` that will suspend when the data is not yet loaded
- leaner client (under alternate entry point)
- Leaner client (under alternate entry point)
- Better types for `useQuery`/`useMutation`/`useSubscription`

## 4.0
Expand Down
28 changes: 27 additions & 1 deletion config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ const defaults = {
const ignoreTSFiles = ".ts$";
const ignoreTSXFiles = ".tsx$";

const react19TestFileIgnoreList = [
ignoreTSFiles,
// The HOCs and Render Prop Components have been deprecated since March 2020,
// and to test them we would need to rewrite a lot of our test suites.
// We will not support them any more for React 19.
// They will probably work, but we make no more guarantees.
"src/react/hoc/.*",
"src/react/components/.*",
];

const react17TestFileIgnoreList = [
ignoreTSFiles,
// We only support Suspense with React 18, so don't test suspense hooks with
Expand All @@ -49,6 +59,17 @@ const tsStandardConfig = {

// For both React (Jest) "projects", ignore core tests (.ts files) as they
// do not import React, to avoid running them twice.
const standardReact19Config = {
...defaults,
displayName: "ReactDOM 19",
testPathIgnorePatterns: react19TestFileIgnoreList,
moduleNameMapper: {
"^react$": "react-19",
"^react-dom$": "react-dom-19",
"^react-dom/(.*)$": "react-dom-19/$1",
},
};

const standardReact18Config = {
...defaults,
displayName: "ReactDOM 18",
Expand All @@ -69,5 +90,10 @@ const standardReact17Config = {
};

module.exports = {
projects: [tsStandardConfig, standardReact17Config, standardReact18Config],
projects: [
tsStandardConfig,
standardReact17Config,
standardReact18Config,
standardReact19Config,
],
};
2 changes: 1 addition & 1 deletion docs/source/caching/advanced-topics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function Foo (){
export default Foo;
```
## TypePolicy inheritence
## TypePolicy inheritance
JavaScript developers will be familiar with the idea of [inheritance](https://en.m.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) from the `extends` clause of `class` declarations, or possibly from dealing with prototype chains created by `Object.create`.
Expand Down
31 changes: 31 additions & 0 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"inline-inherit-doc": "ts-node-script config/inlineInheritDoc.ts",
"test": "node --expose-gc ./node_modules/jest/bin/jest.js --config ./config/jest.config.js",
"test:debug": "node --inspect-brk node_modules/.bin/jest --config ./config/jest.config.js --runInBand --testTimeout 99999 --logHeapUsage",
"test:ci": "TEST_ENV=ci npm run test:coverage -- --logHeapUsage && npm run test:memory",
"test:ci": "TEST_ENV=ci npm run test:coverage -- --logHeapUsage",
"test:watch": "jest --config ./config/jest.config.js --watch",
"test:memory": "npm i && npm run build && cd scripts/memory && npm i && npm test",
"test:coverage": "npm run coverage -- --ci --runInBand --reporters=default --reporters=jest-junit",
Expand Down Expand Up @@ -164,8 +164,10 @@
"prettier": "3.1.1",
"react": "18.3.1",
"react-17": "npm:react@^17",
"react-19": "npm:[email protected]",
"react-dom": "18.3.1",
"react-dom-17": "npm:react-dom@^17",
"react-dom-19": "npm:[email protected]",
"react-error-boundary": "4.0.13",
"recast": "0.23.6",
"resolve": "1.22.8",
Expand Down
2 changes: 1 addition & 1 deletion src/react/hooks/__tests__/useFragment.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
within,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { act } from "react-dom/test-utils";
import { act } from "@testing-library/react";

import { UseFragmentOptions, useFragment } from "../useFragment";
import { MockedProvider } from "../../../testing";
Expand Down
6 changes: 6 additions & 0 deletions src/react/hooks/__tests__/useLoadableQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import {
useTrackRenders,
} from "../../../testing/internal";

const IS_REACT_19 = React.version.startsWith("19");

afterEach(() => {
jest.useRealTimers();
});
Expand Down Expand Up @@ -4594,6 +4596,8 @@ it('does not suspend deferred queries with partial data in the cache and using a
});

it("throws when calling loadQuery on first render", async () => {
// We don't provide this functionality with React 19 anymore since it requires internals access
if (IS_REACT_19) return;
using _consoleSpy = spyOnConsole("error");
const { query, mocks } = useSimpleQueryCase();

Expand All @@ -4613,6 +4617,8 @@ it("throws when calling loadQuery on first render", async () => {
});

it("throws when calling loadQuery on subsequent render", async () => {
// We don't provide this functionality with React 19 anymore since it requires internals access
if (React.version.startsWith("19")) return;
using _consoleSpy = spyOnConsole("error");
const { query, mocks } = useSimpleQueryCase();

Expand Down
2 changes: 1 addition & 1 deletion src/react/hooks/__tests__/useMutation.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { GraphQLError } from "graphql";
import gql from "graphql-tag";
import { act } from "react-dom/test-utils";
import { act } from "@testing-library/react";
import { render, waitFor, screen, renderHook } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import fetchMock from "fetch-mock";
Expand Down
34 changes: 31 additions & 3 deletions src/react/hooks/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Fragment, ReactNode, useEffect, useRef, useState } from "react";
import { DocumentNode, GraphQLError } from "graphql";
import gql from "graphql-tag";
import { act } from "react-dom/test-utils";
import { act } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { render, screen, waitFor, renderHook } from "@testing-library/react";
import {
Expand Down Expand Up @@ -35,6 +35,8 @@ import {
import { useApolloClient } from "../useApolloClient";
import { useLazyQuery } from "../useLazyQuery";

const IS_REACT_19 = React.version.startsWith("19");

describe("useQuery Hook", () => {
describe("General use", () => {
it("should handle a simple query", async () => {
Expand Down Expand Up @@ -1557,7 +1559,33 @@ describe("useQuery Hook", () => {

function checkObservableQueries(expectedLinkCount: number) {
const obsQueries = client.getObservableQueries("all");
expect(obsQueries.size).toBe(2);
/*
This is due to a timing change in React 19

In React 18, you observe this pattern:

1. render
2. useState initializer
3. component continues to render with first state
4. strictMode: render again
5. strictMode: call useState initializer again
6. component continues to render with second state

now, in React 19 it looks like this:

1. render
2. useState initializer
3. strictMode: call useState initializer again
4. component continues to render with one of these two states
5. strictMode: render again
6. component continues to render with the same state as during the first render

Since useQuery breaks the rules of React and mutably creates an ObservableQuery on the state during render if none is present, React 18 did create two, while React 19 only creates one.

This is pure coincidence though, and the useQuery rewrite that doesn't break the rules of hooks as much and creates the ObservableQuery as part of the state initializer will end up with behaviour closer to the old React 18 behaviour again.

*/
expect(obsQueries.size).toBe(IS_REACT_19 ? 1 : 2);

const activeSet = new Set<typeof result.current.observable>();
const inactiveSet = new Set<typeof result.current.observable>();
Expand All @@ -1578,7 +1606,7 @@ describe("useQuery Hook", () => {
}
});
expect(activeSet.size).toBe(1);
expect(inactiveSet.size).toBe(1);
expect(inactiveSet.size).toBe(obsQueries.size - activeSet.size);
}

checkObservableQueries(1);
Expand Down
3 changes: 2 additions & 1 deletion src/react/hooks/__tests__/useReactiveVar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { makeVar } from "../../../core";
import { useReactiveVar } from "../useReactiveVar";

const IS_REACT_18 = React.version.startsWith("18");
const IS_REACT_19 = React.version.startsWith("19");

describe("useReactiveVar Hook", () => {
it("works with one component", async () => {
Expand Down Expand Up @@ -277,7 +278,7 @@ describe("useReactiveVar Hook", () => {
);

await waitFor(() => {
if (IS_REACT_18) {
if (IS_REACT_18 || IS_REACT_19) {
expect(mock).toHaveBeenCalledTimes(3);
expect(mock).toHaveBeenNthCalledWith(1, 0);
expect(mock).toHaveBeenNthCalledWith(2, 0);
Expand Down
13 changes: 9 additions & 4 deletions src/react/hooks/__tests__/useSuspenseQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9591,9 +9591,14 @@ describe("useSuspenseQuery", () => {

await act(() => user.type(input, "ab"));

await waitFor(() => {
expect(screen.getByTestId("result")).toHaveTextContent("ab");
});
await waitFor(
() => {
expect(screen.getByTestId("result")).toHaveTextContent("ab");
},
{
timeout: 10000,
}
);

await act(() => user.type(input, "c"));

Expand All @@ -9612,7 +9617,7 @@ describe("useSuspenseQuery", () => {
await waitFor(() => {
expect(screen.getByTestId("result")).toHaveTextContent("abc");
});
});
}, 10000);

it("works with startTransition to change variables", async () => {
type Variables = {
Expand Down
Loading

0 comments on commit 9eaa883

Please sign in to comment.