apollo-feature-requests
apollo-feature-requests copied to clipboard
Feature: Full React layer rewrite
This issue tracks the rewrite of Apollo Client's React integration layer, covering many areas such as:
- Ensuring better communication with the AC core (by making the React layer lighter weight)
- Removing
graphqlHOC and render prop approaches - [x] apollographql/apollo-client#9627
- Reconsidering our supported hooks (add
useFragment, consider removinguseLazyQuery, etc.) - Existing hook API changes (better async-ness, reduce/eliminate
onError/onCompletedcallback use, etc.) - Remove
partialRefetch
To help get some of this work out sooner (especially React 18 compatibility changes), we're going to tackle the non-breaking parts of this work in Apollo Client 3.5. Progress will be tracked here: https://github.com/apollographql/apollo-client/issues/8419
Hey @hwillson, I know it might not be the place to discuss that but what's with the removing useLazyQuery part ? Should we not use it anymore ? Thx
@htulipe Weβre probably not going to remove useLazyQuery() in the foreseeable future. That was mostly just brainstorming about potential breaking changes we wanted to make (never let a major version go to waste).
Thk for the clarification @brainkim
For people who want to already use React.Suspense together with hooks, you could try this in the meantime: https://gist.github.com/llamadeus/ff8ffe7ac156e545575dad81142f1b6f
Edit: Use kanitsharma/react-suspender.
@hwillson i see this was removed from being "Planned" in "Release 4.0"
Completely get this is a massive undertaking but with React 18 now in beta, would you be able to share any sort of timeline for official suspense support?
There's an RC-1 release for react 18 https://www.npmjs.com/package/react/v/18.0.0-rc.1
Any updates on a timeline for suspense support?
I'm also curious. Now I'm using React 18 on prod and made Apollo SSR work with Lazy for bundle splitting. I wonder if it'll offer me any significant improvement.
Update would be highly appreciated. That radio silence is concerning and don't make planning updating our app to React 18 any easier.
Hi, today is the React 18 release day and I would like to have an idea of how you're planing to move forward with this task? I would love to be able to use suspense to deal with loading states.
@hwillson not expecting anything to be ready tomorrow but would really appreciate at least an indication of possible timelines. We are trying to see where react 18 upgrade fits into our year.
@nathanmarks we're on it! The good news is we've had React 18 compatibility ready for a while; the bad news is that we just pulled that compatibility out of the 3.6 branch so we could publish 3.6 (since R18 wasn't final yet), which means we now need to add it back in. π
@hwillson great -- what about support for things like suspense?
@hwillson two years and half the community is waiting for Suspense support for useQuery
https://github.com/apollographql/apollo-feature-requests/issues/162
Do you have any update about it?
I just think Apollo-client awesome and don't want to drop the use of it in my projects, but without a clear schedule when this feature will be done, unfortunately I'll have to drop using it.
I'm really curious how you guys achieve the functionality of useLazyQuery if you are discussing the possibility of removing it.
I'm really curious how you guys achieve the functionality of
useLazyQueryif you are discussing the possibility of removing it.
https://github.com/apollographql/apollo-feature-requests/issues/366
I'm really interesting in concurrent features so
After a a few tries a get this useSuspendedQuery working
It is still some options mapping a refetch functions and stuff like this it works What do you guys think?
import {
DocumentNode,
OperationVariables,
QueryHookOptions,
TypedDocumentNode,
useApolloClient,
ApolloQueryResult,
} from "@apollo/client";
import { useRef, useState, useSyncExternalStore } from "react";
function deepEqual(objA: any, objB: any, map = new WeakMap()): boolean {
// P1
if (Object.is(objA, objB)) return true;
// P2
if (objA instanceof Date && objB instanceof Date) {
return objA.getTime() === objB.getTime();
}
if (objA instanceof RegExp && objB instanceof RegExp) {
return objA.toString() === objB.toString();
}
// P3
if (
typeof objA !== "object" ||
objA === null ||
typeof objB !== "object" ||
objB === null
) {
return false;
}
// P4
if (map.get(objA) === objB) return true;
map.set(objA, objB);
// P5
const keysA = Reflect.ownKeys(objA);
const keysB = Reflect.ownKeys(objB);
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
if (
!Reflect.has(objB, keysA[i]) ||
!deepEqual(objA[keysA[i]], objB[keysA[i]], map)
) {
return false;
}
}
return true;
}
export function useSuspendedQuery<TData = any, TVariables = OperationVariables>(
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
props?: QueryHookOptions<TData, TVariables>
): ApolloQueryResult<TData> {
const client = useApolloClient();
const snapShotCache = useRef<ApolloQueryResult<TData>>();
const [observedQuery] = useState(() => {
const obsQuery = client.watchQuery<TData, TVariables>({ query, ...props });
return obsQuery;
});
const data = useSyncExternalStore(
(store) => {
const unSub = observedQuery.subscribe(() => {
store();
});
return () => {
unSub.unsubscribe();
};
},
() => {
const result = observedQuery.getCurrentResult();
const isEqual = deepEqual(snapShotCache.current, result);
const newValue = (
isEqual ? snapShotCache.current : result
) as ApolloQueryResult<TData>;
if (!isEqual) {
snapShotCache.current = newValue;
}
console.log("isEqual", isEqual);
console.log("hasData", Boolean(newValue.data));
return newValue;
}
);
const cache = client.readQuery<TData, TVariables>({ query, ...props });
if (!cache) {
const { fetchPolicy, ...newProps } = props ?? {};
const policy = client.defaultOptions.query?.fetchPolicy;
throw client.query({
query,
...newProps,
fetchPolicy: policy,
});
}
return data;
}
Hey all π
Reading back through this after a while, I was pleasantly surprised to see that we actually hit on all of these points for Apollo Client 4.0.
- β Ensuring better communication with the AC core (by making the React layer lighter weight)
useQuery is 75% smaller in production in v4 (Apollo Client 4 in general is ~20% smaller)
- β
Removing
graphqlHOC and render prop approaches
HOCs and the query components were removed in v4
- β [x] apollographql/apollo-client#9627
Apollo Client 3.8 added support for the first suspense-enabled hooks with more to follow through the 3.x releases.
- β
Reconsidering our supported hooks (add
useFragment, consider removinguseLazyQuery, etc.)
useFragment was introduced in 3.7 and we've got a PR to add support for arrays with useFragment in 4.1 (https://github.com/apollographql/apollo-client/pull/12971). useLazyQuery was rewritten for v4 to have more predictable behavior.
- β
Existing hook API changes (better async-ness, reduce/eliminate
onError/onCompletedcallback use, etc.)
onCompleted/onError were removed in v4
- β
Remove
partialRefetch
Removed in v4
As such, I believe this is complete! Very happy to close this one π. For those that want to upgrade to v4, please see our migration guide for detailed instructions on how to upgrade. Thanks!