-
Notifications
You must be signed in to change notification settings - Fork 786
v2.1 Implemented the Query Component #1398
v2.1 Implemented the Query Component #1398
Conversation
Generated by 🚫 dangerJS |
I definitely like the render callback as a child like i18next uses, it makes the code cleaner with an end tag. const Component = () => (
<Query
query={query}
loading={() => <div>Loading</div>}
error={error => <div>{error}</div>}
>
{result => {
return <div>Data:)</div>
}}
</Query>
) |
src/Query.tsx
Outdated
OptionProps, | ||
} from './types'; | ||
|
||
type Props = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an interface and it will need to be exported when tsc -d
runs and creates the .d.ts
file. I usually name these after the component so they are more useful in autocomplete, such as QueryProps
here - aids is composition of any components that might use these.
src/Query.tsx
Outdated
}; | ||
_updateCurrentData = () => { | ||
this.setState({ result: this.queryObservable.currentResult() }); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these methods, instead of underscore, use private updateCurrentData
to scope them. I may PR a tslint update that will auto fix the current codebase.
src/Query.tsx
Outdated
return error(result.error); | ||
} | ||
|
||
return render(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I much prefer the render callback to execute children
, as noted in general comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refer to my comment in #1399 about my thoughts here. I will leave it as a render prop for now and await feedback from some more people before updating anything
Thanks for taking time to work on this, definitely something I would like to see. |
Hey @rosskevin, thanks a lot for your feedback, this helped improve the code! I really appreciate it:) I have created an example project illustrating the usage of the new component under |
@excitement-engineer now that we are both committers, we should probably hash out with @jbaxleyiii the style choice on the render callback. I'm firmly in the camp of using My argument is simple: Counter arguments? |
Generated by 🚫 dangerJS |
Hey @rosskevin @jbaxleyiii ! I have added support for both a render-prop and children render function. A lot of libraries support both conventions and now people can choose the one they prefer. What do you think? I think we also need to take a look at the loading, and error render callback. I think that they add unnecessary complexity to the API whilst their benefit is not immediately clear to me. Consider: <Query
query={HERO_QUERY}
loading={() => <div>Loading</div>}
error={() => <h1>ERROR</h1>}
render={result => <div>data</div>}
/> vs <Query
query={HERO_QUERY}
render={result => {
const { loading, error, data } = result;
if (loading) {
return <div>Loading</div>;
}
if (error) {
return <h1>ERROR</h1>;
}
return <div>data</div>;
/> I think that it is important to keep the API as simple as possible for users. Therefore, I am leaning towards the second API (without the loading and error callback). Any thoughts? |
Also, should we go ahead and merge this PR and continue on refining in smaller follow-up PRs? |
@excitement-engineer Given the fact that |
Good point @leoasis, I think that a single prop render callback is better in this case then! Thanks for your comment |
I am also wondering whether can simplify the API further. Instead of using an <Query
query={HERO_QUERY}
options={{
variables: {
episode,
},
}}
render={result => null}
/> We could do: <Query
query={HERO_QUERY}
variables={{episode}}
render={result => null}
/> Thoughts? |
@excitement-engineer I think that's a good idea, since there are not that many options anyway, and now that we have the props as the component API we can use that. Also some options, such as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for your work on this. I added a few notes - I feel strongly we should pick an implementation (prop) to prevent a larger API surface area and confusion from users. As I mentioned previously, children
is the the prop everyone already knows that React renders, so while some others have chosen the render
prop explicitly, I think it is redundant and the wrong path. If someone really wants that in their code, they can wrap our component and convey their render
prop to our children
prop. Also, being really picky about type names now will save us loads of issues and user confusion later as well.
src/Query.tsx
Outdated
skip?: Boolean; | ||
loading?: () => React.ReactNode; | ||
error?: (error: ApolloError) => React.ReactNode; | ||
render?: ((result: QueryRenderProp) => React.ReactNode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of omitting this
src/Query.tsx
Outdated
loading?: () => React.ReactNode; | ||
error?: (error: ApolloError) => React.ReactNode; | ||
render?: ((result: QueryRenderProp) => React.ReactNode); | ||
children?: ((result: QueryRenderProp) => React.ReactNode) | React.ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be function only, no | React.ReactNode
- it only confuses the possibilities.
src/Query.tsx
Outdated
|
||
export interface QueryProps { | ||
query: DocumentNode; | ||
options?: QueryOpts; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree that we should flatten the options here - no need for a nested options object.
src/Query.tsx
Outdated
import { QueryOpts, OperationVariables } from './types'; | ||
import { parser, DocumentType } from './parser'; | ||
|
||
export interface QueryRenderProp { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should rename this type to prevent confusion. I suggest:
QueryResult
(preferred)QueryRenderArgs
Hey @rosskevin. Thanks a lot for your review! I will update the PR accordingly. You have convinced me about using children as the render callback, I have removed the |
With regards to simplicity of the API, you put up a good point @leoasis, I think you are right about being able omit the |
I am also wondering if there is a use case for including an HOC version of the |
src/Query.tsx
Outdated
|
||
return null; | ||
const renderedChildren = children(result); | ||
return renderedChildren && React.Children.only(renderedChildren); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still have to have this limitation with React 16? We could allow more than one resulting child since now React allows it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with limiting to 16+ - but I'm on the progressive side. Not sure how other users would feel about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say yes, since this will be part of a new version, a new API. And even then, you can still use it in previous versions, only that it will fail with a less clear error when you attempt to render multiple children than the one React.Children.only
gives you
@excitement-engineer I had a long discussion about this in material-ui - I don't want to repeat so I'll link: mui/material-ui#9503 (comment) TL;DR - yes, we also want a HOC that wraps the render callback. There are few cases where it is a better user experience, but still useful in those cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, let's get these final things sewn up (like build errors) and I'm going to try it right away in our app. We are actively migrating from react-relay and would rather use render callbacks!
src/Query.tsx
Outdated
fetchPolicy?: FetchPolicy; | ||
pollInterval?: number; | ||
notifyOnNetworkStatusChange?: boolean; | ||
children?: (result: QueryResult) => React.ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Children is required, so drop the ?
here.
src/Query.tsx
Outdated
|
||
render() { | ||
const { children } = this.props; | ||
const result = this.getRenderProps(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just put getRenderProps
implementation into render. Alternatively you could keep it and rename it to getResult
to be consistent.
@excitement-engineer we can do the HOC in a different PR - I'm anxious to merge and start using to get this thing bulletproofed. |
@excitement-engineer I updated your branch with Pull the update then run |
Hey @rosskevin and @leoasis. I incorporated your feedback, thanks! I will get the build to pass and then we can merge this thing for testing purposes. |
Regarding HOCs. In the mui comment you said:
I'm curious what the concrete use cases are where HOC is better than a render prop. I think that we need to be careful about including an HOC in the library especially for newcomers to the React world to whom the choice will only cause confusion. We can discuss in a follow-up PR thought:) |
The use case (for this) would be where the child is a |
@excitement-engineer I wonder if you are fighting with |
Yeah I am having issues with linting. Prettier is automatically adding semicolons to the private functions in the react component which in turn causes the linting to given an error than an unnecessary semicolon has been added. |
I am checking if there is some linting rule that we can turn off in the |
I'd like to make changes to our lint. In these cases, I think we should trust one and make the other lenient. I propose semicolons are the responsibility of prettier (and easier that way to make bulk changes), so tslint should become agnostic on the subject. |
Completely agree, I think that we should let prettier do what it is good at: formatting, and let typescript do the rest. |
I have pushed another commit, let's see if this one passes. |
Just got to add a changelog and we are good to go |
@excitement-engineer your |
Oh I didn't see that, thanks! I will do so as soon as I find some time. |
@excitement-engineer @rosskevin Is there a new approach to I'm trying to use the new component, but my existing query needs to set that option to
|
@excitement-engineer @rosskevin Does the Query component need the If I'm simply not understanding the new approach to errorPolicy, let me know. Thanks! |
I was checking out the Roadmap for v2.1 of React-Apollo and I am really excited about all the features coming in the next version, so I decided to implement some of the ideas described:)
The Roadmap describes the
Query
component that will allow use of this library without resorting to HOC. This PR provides an implementation of thisQuery
component in line with the API described in the roadmap.I have implemented most of the features that I think the
Query
component will need. I would love to hear some feedback on what I missed and on how to improve the API!As an example, you can now interact with apollo like this:
I have implemented a fair amount of tests for the component but it is not complete yet, this PR is still very much a WIP. In addition, I have not tried it yet in an actual implementation yet, however I will implement an new repo in the examples folder that will make use of this API to test it out .
I would love to hear what you think!