Conversation
Looks great! |
@tmeasday |
I'd change the Maybe something like |
One thing missing from this API is a way to fetch a totally different query based on the props. Before, you could simply not fetch a query if a certain prop wasn't fetched, but now as far as I can tell you will always fetch the document and then only thing you can control is the options. Maybe that means we need a new option like |
AFAIK GraphQL servers error if they get unused variables - so I'm not sure if passing props as variables by default will be the right approach?
These methods are on the observable now, just a heads up.
I think it's important to be able to show a loading state while a mutation is in progress. What would be the way to do that here? having said that, WOW this is super dope. |
That sounds great to me. How do you feel about
Ah yes, we actually run into this often.
This wasn't clear enough in the api, my plan is to use
👍 I've got to play some catchup on all the awesome stuff in the client now!
In practice, we have been using |
FWIW, between these two:
I would suggest that:
is most similar to the That said, I kind of like the simplicity of p.s. I'm so excited to start using this! Awesome stuff! |
Yeah having a really short and sweet name can be nice :) people can always use ES2015 import renaming if they want to (not that most people are aware of that ES2015 feature though) |
@stubailo are you okay with |
@stubailo thoughts on the being the 1.0 (after a through RC testing) so align with the 1.0 of the client? |
The initial release of this is ready for testing! The release candidate can be installed using |
I don't think we are going to ship 1.0 of apollo-client until late september at the earliest, so I'd say it's a bit early. I'd rather ship all the features ahead of time, do a ton of testing, and then change the number at the last moment. IMO 1.0 draws a lot of attention so you don't want to be making major changes in a 0.x -> 1.0 transition, it's mostly a marketing move. |
Ah, that makes sense. I'll downgrade this back to the 0.4.0 |
@jbaxleyiii just FYI have been looking at this and plan on digging a lot deeper very very soon. |
@tmeasday perhaps we can try out the release candidate in Galaxy/GitHunt? If it works there then it should be pretty good |
@jbaxleyiii. Took a closer look and worked on converting GitHunt over to the new API: https://github.com/apollostack/GitHunt/pull/72/files First off, it's a big improvement! Some comments / issues in order of immediacy:
|
Another thing:
|
|
@tmeasday thanks so much for checking this out!
Good catch, the API design did not match the built product. I've adjusted the code to match the API as I agree, it is much nicer
The result of the
I'd love to know more, I really like this API and would love to know how it could be better / change with changes to the client
Fixed and test added!
This is actually a bug in the client. This should fix that. Once it does, I'll add a test here to ensure it works |
To clarify this, people often reach out to us for help because we have the Apollo Client core API where you call We were talking yesterday and it seems like if we added the concept of a "component" to the core, then react-apollo's job could simply be to initialize a "component" object, then that could have an idempotent function to basically set query options on it. Then But either way we don't think this needs to be done right away - just something we are going to think about in the next month. |
Please ignore the above, total baloney. |
Looking good @jbaxleyiii (check out https://github.com/apollostack/githunt/tree/react-apollo-next). One final thing: how can we do a parameterized You can see that we don't have access to the variables in the mutation here: https://github.com/apollostack/GitHunt/blob/react-apollo-next/ui/CommentsPage.js#L212-220 I would suggest a good solution to the problem might look like my 2. above? Or else maybe the variables could be passed to the |
@jbaxleyiii here is a little more color on what @stubailo was saying above. The final part is just my thoughts on what it could look like, and I love everyone's thoughts on it all. |
@jbaxleyiii did you have any thoughts about the |
@jbaxleyiii one minor thing that's kind of unfortunate is needing to use I wonder if there's a way to get a nicer API for that use case (I also imagine it'll be really easy to leave out the graphql(gql`...`, {
mapPropsToOptions(props) { return {...}; }.
mapResultsToProps({ loading, something }) { return { ... } )
}); (As an example, I was putting together an example for the apollostack website here: https://github.com/apollostack/frontpage-react-app/blob/ffab743608b9f3c95a09247f7a76d6f1d9b89c4e/src/App.js#L38) |
@tmeasday you know I had been wondering the same thing. I think it's a great idea and it enforces the names of the methods which will make documentation and training better. I'll make that change today |
@tmeasday I've adjusted the API (and the design of it above) to allow for an argument instead of positional arguments. It also allows you to pass an optimistic result as part of the mutation action (mutations now take a mutation option instead of just variables.) Let me know if this solves the missing pieces! |
@jbaxleyiii I don't know if you saw @tmeasday's question above:
|
@stubailo yep! You can now pass in an optimisticResponse when calling the mutation which would allow you to create it with any variables you want |
I did it this way so you could pass user input, component state, whatever as a response |
Sounds great to me. Tom is out for a few minutes, when he comes back let's get a final OK and then we're probably ready to ship! |
Oh OK, I'll let you guys figure it out! |
API adjustments made, and added a test for redux usage in conjunction with |
So how would I provide both query and mutation to one component? Say I have a form component
Or you're saying that one should always have two separate components for queries and mutations?
How would I provide initial data to MyFormEdit? |
@Vanuan you have a few options: import React, { Component } from 'react';
import { graphql } from 'react-apollo';
class MyComponent extends Component { ... }
const MyComponentWithMutateAndData = graphql(UPVOTE)(graphql(DOWNVOTE)(MyComponent)); or with decorators import React, { Component } from 'react';
import { graphql } from 'react-apollo';
@graphql(UPVOTE)
@graphql(DOWNVOTE)
class MyComponent extends Component { ... } or with custom names: import React, { Component } from 'react';
import { graphql } from 'react-apollo';
class MyComponent extends Component { ... }
MyComponent.propTypes = {
upvote: React.PropTypes.func.isRequired,
downvote: React.PropTypes.func.isRequired,
};
// This provides an `upvote` callback prop to `MyComponent`
const MyComponentWithUpvote = graphql(UPVOTE, {
name: 'upvote',
})(MyComponent);
// This provides an `downvote` callback prop to `MyComponentWithUpvote`,
// and subsequently `MyComponent`
const MyComponentWithUpvoteAndDownvote = graphql(DOWNVOTE, {
name: 'downvote',
})(MyComponentWithUpvote); |
Ok, so it boils down to "use multi-layer wrapping". I see. |
Guys this is amazing! congrats on the new release! |
API overview
react-apollo
exposes three top level items for the client (and two for the server) which, when used in conjunction, make for easily binding graphql actions to react components. The intent of this library is to make co-location of graphql data simple, and mutations easy to use within components.ApolloProvider
Modeled after
react-redux
, this is a component which binds the created client fromApolloClient
to the store. It can be used as a drop in replacement for theProvider
fromreact-redux
, or used in with it.Basic apollo version:
Used in conjunction with a redux store:
Used in conjunction with react-redux
Provider
:withApollo
is a HOC higher order component which provides access to your apollo-client as a prop to your wrapped component. This is useful if you want to do custom logic with apollo, without using thegraphql
HOC.graphql
graphql
is a HOC higher order component which allows you to declare a graphql operation (mutation or query) and have the data or actions it represents bound to the wrapped component. For queries, this meansgraphql
handles the fetching and updating of information from the query using apollo'swatchQuery
method. For mutations,graphql
binds the intended mutation to be called using apollo.The signature of
graphql
is as follows:Lets break that apart..
HOC
graphql(/* arguments */)(Component)
in keeping with the HOC model,graphql
should be used with either a react class, or a stateless component. It should wrap the component using either function syntax or as a decorator. For example:Both of the above will return a wrapped component which will get the benefit of graphql integration.
Document
The first, and only required, argument of
graphql
is a graphql document. This cannot be a string so using a library likegraphql-tag
or a compilation step is recommended. This can either be aquery
or amutation
but not both.Options:
The options argument allows for customizing the options of the operation, and customizing the shape of the child component. It has the following signature:
options
The
options
method allows you to map the props passed from the parent component to create either options forwatchQuery
ormutate
. The default is to passprops
as the variables key in both option types ((props) => ({ variables: props })
).Using the default method will expect an id key to exist on the props:
Using a custom mapping method which returns a computed option definition
Passing other option definitions
The
props
method allows you to customize the props passed to the child component based on resulting data. It takes a method which receives the result object (which includes the data, the loading state, any errors, and client methods) and expects an object in return. This is similar toreact-redux
'smapStateToProps
method.name
If you want to customize the name of the resulting props (instead of
data
ormutate
) you can use a custom name via thename
field on the options. If you pass aprop
method, it will overwrite this name. This is particularly helpful for multiple operations on a single component.Queries
Using
graphql
with queries makes it easy to bind data to components. As seen above,graphql
will assign the result of the query asdata
to the props passed to the wrapped component. The shape of the result will be the following:loading: Boolean
Loading will be true if a query is in flight (including when calling refetch)
error: ApolloError
The error key will be
null
if no errors were created during the query...queries
graphql
will merge the root queries used within the query document executed. This allows for multiple root queries to be located on the top level of the result. For instance:will return a result object that includes
{ data: { name: "James" }, likes: { count: 10 } }
....QuerySubscription
The subscription created on this query will be merged into the passed props so you can dynamically refetch, change polling settings, or even unsubscribe to this query.
query
Sometimes you may want to call a custom query within a component. To make this possible,
graphql
passes the query method from ApolloClient as a propmutate
Sometimes you may want to call a custom mutation within a component. To make this possible,
graphql
passes the mutate method from ApolloClient as a propMutations
Using
graphql
with mutations makes it easy to bind actions to components. Unlike queries, mutations return only a single method (the mutation method) to the wrapped component. When calling a mutation, you can pass an options that can be passed to apollo-client mutation. Mutations will be passed to the child asprops.mutate
Server methods
react-apollo supports integrated server side rendering for both store rehydration purposes, or fully rendered markup.
getDataFromTree
The
getDataFromTree
method takes your react tree and returns the context of you react tree. This can be used to get the initialState viacontext.store.getState()
The
renderToStringWithData
takes your react tree and returns the stringified tree with all data requirements. It also injects a script tag that includeswindow. __APOLLO_STATE__
which equals the full redux store for hydration. This method returns a promise that eventually returns the markup