Conversation
8944d5c to
248b773
Compare
958baf5 to
9ddaf5a
Compare
| @@ -1,4 +1,4 @@ | |||
| export {ApolloProvider} from './ApolloProvider'; | |||
| export {useApolloClient} from './apollo-client'; | |||
| export {default as useApolloClient} from './apollo-client'; | |||
There was a problem hiding this comment.
decided to export this so we can use it instead of withApollo
packages/react-async/src/Async.tsx
Outdated
|
|
||
| import {AsyncAssetContext, AsyncAssetManager} from './context/assets'; | ||
| import {normalize, resolve} from './utilities'; | ||
| import {initialResolved, resolve} from './utilities'; |
There was a problem hiding this comment.
I think maybe trySynchronousResolve provides the explicitness that we kind of want here, given that it is a bit of an odd process
| } | ||
|
|
||
| export function initialResolved<T>({id, defer}: Options<T>): T | null { | ||
| const canResolve = defer == null && id; |
There was a problem hiding this comment.
I think defer can also be DeferTiming.Mount and we'd still want to do this
|
|
||
| export function initialResolved<T>({id, defer}: Options<T>): T | null { | ||
| const canResolve = defer == null && id; | ||
| const resolved = canResolve && id ? tryRequire(id()) : null; |
There was a problem hiding this comment.
&& id is not needed given the check above, so remove it from one
|
|
||
| ### `useApolloClient` | ||
|
|
||
| `useApolloClient` hook can be use to access apollo client that is currently in the context. |
There was a problem hiding this comment.
What do you think about calling it useGraphQLClient instead? I am not sure if it's better to hide Apollo's involvement or not here...
There was a problem hiding this comment.
we do call this lib react-graphql ...
I can go both way of this, useApolloClient make it so people know exactly they are getting the client object from apollo and should look up apollo API on how to use it.
useGraphQLClient hides that, but is future proof if we ever not use apollo?
There was a problem hiding this comment.
Who knows about the Apollo thing, I think this is fine though (renaming a function is a fairly easy refactor later anyways) 👍
packages/react-graphql/README.md
Outdated
| ### `useApolloClient` | ||
|
|
||
| `useApolloClient` hook can be use to access apollo client that is currently in the context. | ||
| Which can be use to trigger query manually. |
packages/react-graphql/src/async.tsx
Outdated
| Object.defineProperty(FinalComponent, 'resolved', { | ||
| get: () => resolvedDocument, | ||
| // accessor descriptor don't need this | ||
| // writable: false, |
| async () => { | ||
| if (!isDocumentNode(documentOrComponent)) { | ||
| try { | ||
| const resolved = await documentOrComponent.resolve(); |
There was a problem hiding this comment.
I think we need to check whether the component is mounted before doing this? I wrote a nice hook for this we can throw in the shared package:
export function useMounted() {
const mounted = useRef(true);
useEffect(() => {
return () => {
mounted.current = false;
};
}, []);
return mounted;
}There was a problem hiding this comment.
arg...I would have assume that react have this behaviour imbedded in all the hooks now. (don't know why I thought that...)
There was a problem hiding this comment.
@lemonmade curious as to why you are using a ref in the hook above instead of a variable? I can see how useRef can be useful if the value being assigned was an object since we can re-use the same reference without creating new object references, however, if it is a boolean, it should be fine to use let mounted ?
There was a problem hiding this comment.
I think we need to check whether the component is mounted before doing this?
I likely don't have context, but why do we need to check for mounted ? Won't this function useGraphQLDocument not called at all if the component using it is not mounted? Feels like I am missing context 🤔
There was a problem hiding this comment.
Yes, the other option here is just to let mounted = true;, then set it to false in the callback, then check that variable in scope after the async operation. The hook just wraps that logic up (the only way to wrap it up in a hook is to do it with a ref, you can't really store a persisted value any other way).
Yes, useEffect only gets called when the component is mounted, but that doesn't mean it will end while the component is still mounted. If you kick off an async operation (as is done here), there is no requirement for React to wait for it to be done while the component is still mounting (it would be catastrophic if the UI didn't update until the promise resolved, and it would make no sense to keep the component mounted beyond when a parent unmounts it).
There was a problem hiding this comment.
so what to know is this setState is after an await
the component will be mount when the async method start, but may not be mounted by the time it finished.
Ref is normally uses for accessing the dom, but in hooks land, it is also the react recommended way to keeping value you will normally keep around using property variable in class component.
Since hook turn the function itself into a component that has lifecycle, just using a variables that can be access through out the different hook can have unexpected result.
There was a problem hiding this comment.
The is literally the exact same problem solved by having a mounted instance variable on a class component.
There was a problem hiding this comment.
Makes sense. Thanks to you both!
As for the mounted ref, an ideal solution would have been to have some kind of cancel/abort for loadDocument which we return from the useEffect so that it cancels the document load if the component unmounts. Having said that, I can't think of a reliable way to do so for this use case.
There was a problem hiding this comment.
Yes, it would definitely be preferable, we can potentially wrap in a cancellable promise as an alternative (I suggested a mounted ref because I had already written it, a cancellable promise is a little trickier)
| [skip, queryObservable], | ||
| ); | ||
|
|
||
| const previousData = useRef<QueryHookResult<Data, Variables>['data']>(); |
There was a problem hiding this comment.
Don't need an initial value here?
|
|
||
| describe('document', () => { | ||
| it('returns loading=true and networkStatus=loading during the loading of query', async () => { | ||
| const MockQuery = ({children}) => { |
6895705 to
5da5726
Compare
|
Still have the following to do: Todo:
|
7af9df9 to
ec53193
Compare
| function initialState<Value>(props: Props<Value>): State<Value> { | ||
| const canResolve = props.defer == null && props.id; | ||
| const resolved = canResolve && props.id ? tryRequire(props.id()) : null; | ||
| const resolved = trySynchronousResolve<Value>({ |
There was a problem hiding this comment.
FWIW, you can usually just pass props to this function directly, it has more properties but that's fine because it matches the type signature
|
|
||
| ### `useApolloClient` | ||
|
|
||
| `useApolloClient` hook can be use to access apollo client that is currently in the context. |
There was a problem hiding this comment.
Who knows about the Apollo thing, I think this is fine though (renaming a function is a fairly easy refactor later anyways) 👍
packages/react-graphql/README.md
Outdated
| } | ||
| ``` | ||
|
|
||
| ### with query document |
There was a problem hiding this comment.
For follow up: make sure headings are sentence case
| } else { | ||
| return documentOrComponent.resolved; | ||
| } | ||
| }); |
There was a problem hiding this comment.
I wonder if this is a case where we can break the rule of hooks by short circuiting here. Maybe better as an optimization later, but I think it will at least be an option.
| loadDocument(); | ||
| } | ||
| }, | ||
| [document, documentOrComponent, loadDocument], |
There was a problem hiding this comment.
Don't think you need documentOrComponent here?
| export interface QueryHookResult<Data, Variables> | ||
| extends Omit<QueryResult<Data, Variables>, 'networkStatus' | 'variables'> { | ||
| networkStatus: QueryResult<Data, Variables>['networkStatus'] | undefined; | ||
| variables: QueryResult<Data, Variables>['variables'] | undefined; |
There was a problem hiding this comment.
We can technically offer up better typings for some of these because we can check if the variables have any required keys or not, but for now this is fine 👍
Re-open #642
Close #586
🎩 instructions:
web-proving-groundlocallyyarninweb-proving-groundquilt, doyarn && yarn tophat react-async ../web-proving-groundquilt, doyarn tophat react-hooks ../web-proving-groundquilt, doyarn tophat react-graphql ../web-proving-groundweb-proving-groundand doyarn devlocalhost:8081to see the query & mutation working properly^ kinda cool I can do tophat with two different lib. As long as I do the tophat in the dependency order.