Skip to content

Commit

Permalink
Use import * as React everywhere. (#11000)
Browse files Browse the repository at this point in the history
* Use `import * as React` everywhere.
This prevents an error when importing `@apollo/client` in a React Server component. (see [#10974](#10974))
  • Loading branch information
phryneas authored Jul 7, 2023
1 parent 2ebbd3a commit 1d43ab6
Show file tree
Hide file tree
Showing 18 changed files with 77 additions and 71 deletions.
5 changes: 5 additions & 0 deletions .changeset/large-beers-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@apollo/client': patch
---

Use `import * as React` everywhere. This prevents an error when importing `@apollo/client` in a React Server component. (see [#10974](https://github.com/apollographql/apollo-client/issues/10974))
7 changes: 7 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
"fixStyle": "separate-type-imports"
}],
"@typescript-eslint/no-import-type-side-effects": "error",
"no-restricted-syntax": [
"error",
{
"selector": "ImportDeclaration[source.value='react'][importKind!='type'] :matches(ImportSpecifier, ImportDefaultSpecifier)",
"message": "Please only use the namespace import syntax (`import * as React from 'react'`) for React imports!"
}
],
"import/extensions": [
"error",
"always",
Expand Down
4 changes: 2 additions & 2 deletions .size-limit.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const checks = [
{
path: "dist/apollo-client.min.cjs",
limit: "37976"
limit: "37860"
},
{
path: "dist/main.cjs",
Expand All @@ -10,7 +10,7 @@ const checks = [
{
path: "dist/index.js",
import: "{ ApolloClient, InMemoryCache, HttpLink }",
limit: "33437"
limit: "32181"
},
...[
"ApolloProvider",
Expand Down
2 changes: 1 addition & 1 deletion src/react/hooks/internal/__use.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { wrapPromiseWithState } from '../../../utilities/index.js';
import React from 'react';
import * as React from 'react';

type Use = <T>(promise: Promise<T>) => T;
// Prevent webpack from complaining about our feature detection of the
Expand Down
4 changes: 2 additions & 2 deletions src/react/hooks/internal/useDeepMemo.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { DependencyList } from 'react';
import { useRef } from 'react';
import * as React from 'react';
import { equal } from '@wry/equality';

export function useDeepMemo<TValue>(
memoFn: () => TValue,
deps: DependencyList
) {
const ref = useRef<{ deps: DependencyList; value: TValue }>();
const ref = React.useRef<{ deps: DependencyList; value: TValue }>();

if (!ref.current || !equal(ref.current.deps, deps)) {
ref.current = { value: memoFn(), deps };
Expand Down
6 changes: 3 additions & 3 deletions src/react/hooks/internal/useIsomorphicLayoutEffect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useLayoutEffect, useEffect } from 'react';
import * as React from 'react';
import { canUseDOM } from '../../../utilities/index.js';

// use canUseDOM here instead of canUseLayoutEffect because we want to be able
Expand All @@ -7,5 +7,5 @@ import { canUseDOM } from '../../../utilities/index.js';
// warnings for useSyncExternalStore implementation, canUseLayoutEffect is left
// alone.
export const useIsomorphicLayoutEffect = canUseDOM
? useLayoutEffect
: useEffect;
? React.useLayoutEffect
: React.useEffect;
4 changes: 2 additions & 2 deletions src/react/hooks/internal/useStrictModeSafeCleanupEffect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import * as React from 'react';

export function useStrictModeSafeCleanupEffect(cleanup: () => void) {
let timeout: NodeJS.Timeout;

useEffect(() => {
React.useEffect(() => {
clearTimeout(timeout);

return () => {
Expand Down
4 changes: 2 additions & 2 deletions src/react/hooks/useApolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { invariant } from '../../utilities/globals/index.js';
import { useContext } from 'react';
import * as React from 'react';
import type { ApolloClient } from '../../core/index.js';
import { getApolloContext } from '../context/index.js';

export function useApolloClient(
override?: ApolloClient<object>,
): ApolloClient<object> {
const context = useContext(getApolloContext());
const context = React.useContext(getApolloContext());
const client = override || context.client;
invariant(
!!client,
Expand Down
10 changes: 5 additions & 5 deletions src/react/hooks/useBackgroundQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useMemo, useCallback } from 'react';
import * as React from 'react';
import type {
DocumentNode,
OperationVariables,
Expand Down Expand Up @@ -138,7 +138,7 @@ export function useBackgroundQuery<
client.watchQuery(watchQueryOptions)
);

const [promiseCache, setPromiseCache] = useState(
const [promiseCache, setPromiseCache] = React.useState(
() => new Map([[queryRef.key, queryRef.promise]])
);

Expand All @@ -149,7 +149,7 @@ export function useBackgroundQuery<

useTrackedQueryRefs(queryRef);

const fetchMore: FetchMoreFunction<TData, TVariables> = useCallback(
const fetchMore: FetchMoreFunction<TData, TVariables> = React.useCallback(
(options) => {
const promise = queryRef.fetchMore(options);

Expand All @@ -162,7 +162,7 @@ export function useBackgroundQuery<
[queryRef]
);

const refetch: RefetchFunction<TData, TVariables> = useCallback(
const refetch: RefetchFunction<TData, TVariables> = React.useCallback(
(variables) => {
const promise = queryRef.refetch(variables);

Expand All @@ -177,7 +177,7 @@ export function useBackgroundQuery<

queryRef.promiseCache = promiseCache;

return useMemo(() => {
return React.useMemo(() => {
return [
{ [QUERY_REFERENCE_SYMBOL]: queryRef },
{
Expand Down
4 changes: 2 additions & 2 deletions src/react/hooks/useFragment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef } from "react";
import * as React from "react";
import { equal } from "@wry/equality";

import type { DeepPartial} from "../../utilities/index.js";
Expand Down Expand Up @@ -69,7 +69,7 @@ export function useFragment<
optimistic
};

const resultRef = useRef<UseFragmentResult<TData>>();
const resultRef = React.useRef<UseFragmentResult<TData>>();
let latestDiff = cache.diff<TData>(diffOptions);

// Used for both getSnapshot and getServerSnapshot
Expand Down
12 changes: 6 additions & 6 deletions src/react/hooks/useLazyQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DocumentNode } from 'graphql';
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { useCallback, useMemo, useRef } from 'react';
import * as React from 'react';

import type { OperationVariables } from '../../core/index.js';
import { mergeOptions } from '../../utilities/index.js';
Expand Down Expand Up @@ -29,9 +29,9 @@ export function useLazyQuery<TData = any, TVariables extends OperationVariables
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
options?: LazyQueryHookOptions<NoInfer<TData>, NoInfer<TVariables>>
): LazyQueryResultTuple<TData, TVariables> {
const execOptionsRef = useRef<Partial<LazyQueryHookExecOptions<TData, TVariables>>>();
const optionsRef = useRef<LazyQueryHookOptions<TData, TVariables>>();
const queryRef = useRef<DocumentNode | TypedDocumentNode<TData, TVariables>>();
const execOptionsRef = React.useRef<Partial<LazyQueryHookExecOptions<TData, TVariables>>>();
const optionsRef = React.useRef<LazyQueryHookOptions<TData, TVariables>>();
const queryRef = React.useRef<DocumentNode | TypedDocumentNode<TData, TVariables>>();
const merged = mergeOptions(options, execOptionsRef.current || {});
const document = merged?.query ?? query;

Expand Down Expand Up @@ -60,7 +60,7 @@ export function useLazyQuery<TData = any, TVariables extends OperationVariables
});

// We use useMemo here to make sure the eager methods have a stable identity.
const eagerMethods = useMemo(() => {
const eagerMethods = React.useMemo(() => {
const eagerMethods: Record<string, any> = {};
for (const key of EAGER_METHODS) {
const method = result[key];
Expand All @@ -79,7 +79,7 @@ export function useLazyQuery<TData = any, TVariables extends OperationVariables

Object.assign(result, eagerMethods);

const execute = useCallback<
const execute = React.useCallback<
LazyQueryResultTuple<TData, TVariables>[0]
>(executeOptions => {
execOptionsRef.current = executeOptions ? {
Expand Down
12 changes: 6 additions & 6 deletions src/react/hooks/useMutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import * as React from 'react';
import type { DocumentNode } from 'graphql';
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
import type {
Expand Down Expand Up @@ -32,13 +32,13 @@ export function useMutation<
): MutationTuple<TData, TVariables, TContext, TCache> {
const client = useApolloClient(options?.client);
verifyDocumentType(mutation, DocumentType.Mutation);
const [result, setResult] = useState<Omit<MutationResult, 'reset'>>({
const [result, setResult] = React.useState<Omit<MutationResult, 'reset'>>({
called: false,
loading: false,
client,
});

const ref = useRef({
const ref = React.useRef({
result,
mutationId: 0,
isMounted: true,
Expand All @@ -53,7 +53,7 @@ export function useMutation<
Object.assign(ref.current, { client, options, mutation });
}

const execute = useCallback((
const execute = React.useCallback((
executeOptions: MutationFunctionOptions<
TData,
TVariables,
Expand Down Expand Up @@ -140,13 +140,13 @@ export function useMutation<
});
}, []);

const reset = useCallback(() => {
const reset = React.useCallback(() => {
if (ref.current.isMounted) {
setResult({ called: false, loading: false, client });
}
}, []);

useEffect(() => {
React.useEffect(() => {
ref.current.isMounted = true;

return () => {
Expand Down
18 changes: 6 additions & 12 deletions src/react/hooks/useQuery.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { invariant } from '../../utilities/globals/index.js';

import {
useCallback,
useContext,
useMemo,
useRef,
useState,
} from 'react';
import * as React from 'react';
import { useSyncExternalStore } from './useSyncExternalStore.js';
import { equal } from '@wry/equality';

Expand Down Expand Up @@ -59,7 +53,7 @@ export function useInternalState<TData, TVariables extends OperationVariables>(
client: ApolloClient<any>,
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
): InternalState<TData, TVariables> {
const stateRef = useRef<InternalState<TData, TVariables>>();
const stateRef = React.useRef<InternalState<TData, TVariables>>();
if (
!stateRef.current ||
client !== stateRef.current.client ||
Expand All @@ -75,7 +69,7 @@ export function useInternalState<TData, TVariables extends OperationVariables>(
// setTick function. Updating this state by calling state.forceUpdate is the
// only way we trigger React component updates (no other useState calls within
// the InternalState class).
const [_tick, setTick] = useState(0);
const [_tick, setTick] = React.useState(0);
state.forceUpdate = () => {
setTick(tick => tick + 1);
};
Expand Down Expand Up @@ -159,14 +153,14 @@ class InternalState<TData, TVariables extends OperationVariables> {
// initialization, this.renderPromises is usually undefined (unless SSR is
// happening), but that's fine as long as it has been initialized that way,
// rather than left uninitialized.
this.renderPromises = useContext(getApolloContext()).renderPromises;
this.renderPromises = React.useContext(getApolloContext()).renderPromises;

this.useOptions(options);

const obsQuery = this.useObservableQuery();

const result = useSyncExternalStore(
useCallback(() => {
React.useCallback(() => {
if (this.renderPromises) {
return () => {};
}
Expand Down Expand Up @@ -469,7 +463,7 @@ class InternalState<TData, TVariables extends OperationVariables> {
|| this.observable // Reuse this.observable if possible (and not SSR)
|| this.client.watchQuery(this.getObsQueryOptions());

this.obsQueryFields = useMemo(() => ({
this.obsQueryFields = React.useMemo(() => ({
refetch: obsQuery.refetch.bind(obsQuery),
reobserve: obsQuery.reobserve.bind(obsQuery),
fetchMore: obsQuery.fetchMore.bind(obsQuery),
Expand Down
6 changes: 3 additions & 3 deletions src/react/hooks/useReactiveVar.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { useEffect, useState } from 'react';
import * as React from 'react';
import type { ReactiveVar } from '../../core/index.js';

export function useReactiveVar<T>(rv: ReactiveVar<T>): T {
const value = rv();

// We don't actually care what useState thinks the value of the variable
// is, so we take only the update function from the returned array.
const setValue = useState(value)[1];
const setValue = React.useState(value)[1];

// We subscribe to variable updates on initial mount and when the value has
// changed. This avoids a subtle bug in React.StrictMode where multiple
// listeners are added, leading to inconsistent updates.
useEffect(() => {
React.useEffect(() => {
const probablySameValue = rv();
if (value !== probablySameValue) {
// If the value of rv has already changed, we don't need to listen for the
Expand Down
10 changes: 5 additions & 5 deletions src/react/hooks/useReadQuery.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useState, useMemo, useEffect } from "react";
import * as React from "react";
import invariant from "ts-invariant";
import { NetworkStatus } from "../../core/index.js";
import { QUERY_REFERENCE_SYMBOL, type QueryReference } from "../cache/QueryReference.js";
import { __use } from "./internal/index.js";
import { toApolloError } from "./useSuspenseQuery.js";

export function useReadQuery<TData>(queryRef: QueryReference<TData>) {
const [, forceUpdate] = useState(0);
const [, forceUpdate] = React.useState(0);
const internalQueryRef = queryRef[QUERY_REFERENCE_SYMBOL];
invariant(
internalQueryRef.promiseCache,
Expand All @@ -15,7 +15,7 @@ export function useReadQuery<TData>(queryRef: QueryReference<TData>) {
'Please ensure you are passing the `queryRef` returned from `useBackgroundQuery`.'
);

const skipResult = useMemo(() => {
const skipResult = React.useMemo(() => {
const error = toApolloError(internalQueryRef.result);

return {
Expand All @@ -33,7 +33,7 @@ export function useReadQuery<TData>(queryRef: QueryReference<TData>) {
internalQueryRef.promiseCache.set(internalQueryRef.key, promise);
}

useEffect(() => {
React.useEffect(() => {
return internalQueryRef.listen((promise) => {
internalQueryRef.promiseCache!.set(internalQueryRef.key, promise);
forceUpdate((prevState) => prevState + 1);
Expand All @@ -45,7 +45,7 @@ export function useReadQuery<TData>(queryRef: QueryReference<TData>) {
? skipResult
: __use(promise);

return useMemo(() => {
return React.useMemo(() => {
return {
data: result.data,
networkStatus: result.networkStatus,
Expand Down
Loading

0 comments on commit 1d43ab6

Please sign in to comment.