-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
interaction with domain state and redux #98
Comments
The apollo client itself manages both the reducer and mapping state to props - so rather than using redux-react, you would use https://medium.com/apollo-stack/building-a-graphql-store-from-first-principles-413e19ea65b4 Also, if you have read about Relay, it uses a similar store shape, and it does the same management for you under the hood. The point is, the store needs to be a normalized cache of result objects, rather than something that keeps the result of each query independently. Right now, it doesn't bring as much benefit, but once you start dealing with pagination, optimistic UI, data preloading, refetching, etc - you basically need a normalized store to take advantage of GraphQL. In short, we don't expect people to be using the const Topic = ({ data, loading, loginToken }) => (
<div>
{ loading ? 'Loading...' : <TopicLoaded data={data} loginToken={loginToken} /> }
</div>
);
const TopicWithData = createContainer({
getQuery: ({ params, loginToken }) => {
const maybeLoginToken = loginToken ? `(token: "${loginToken}")` : '';
return `
{
root${maybeLoginToken} {
oneTopic(id: "${params.id}") {
id
category_id
title
posts {
pages {
posts {
username
score
cooked
}
}
}
}
}
}
`;
},
}, Topic); (that's not the final API, just a sketch) |
@abhiaiyer91 one thing about the current store implementation, is there isn't a 1:1 query: data mapping. We normalize the data so using We are working on an easy integration with both redux and react. I'm actually working on the redux api today! Ideally something like this: import { readFromStore } from 'apollo-client'
function mapStateToProps(state) {
return {
job: readFromStore(/.+Job/)
foo: readFromStore(/.+Foo/)
};
} This is a similar proposal syntax to #26. That being said, I'd also like a clean way to say treat this query as a collection of data, normalize it, and let me get the data back out easily. I don't love the regex method so still exploring ideas!
It's worth mentioning the library is still undergoing some API changes (for instance #97) and also doesn't export all of the needed methods yet (for instance |
@jbaxleyiii have you considered using It sounds like writing a React container and demonstrating how I was thinking the data would be used should be a critical priority. |
@stubailo I was just playing around with that 👍. I'm working on redux and react implementations today as we continue moving our app over. I think I get what you are thinking with your example and have created a couple implementations to try it out. Its working great so far. |
Here's my current naive implementation: import React from 'react';
import { ApolloClient } from 'apollo-client';
import { createNetworkInterface } from 'apollo-client/lib/src/networkInterface';
import PureRenderMixin from 'react-addons-pure-render-mixin';
export const client = new ApolloClient();
export function createContainer(options = {}, Component) {
const {
getQuery,
pure = true
} = options;
if (! client) {
throw new Error('need to pass in apollo client');
}
const mixins = [];
if (pure) {
mixins.push(PureRenderMixin);
}
/* eslint-disable react/prefer-es6-class */
return React.createClass({
displayName: 'ApolloContainer',
mixins,
getInitialState() {
return {
loading: true,
};
},
componentDidMount() {
this.runMyQuery(this.props);
},
componentWillReceiveProps(nextProps) {
this.queryHandle.stop();
this.runMyQuery(nextProps);
},
runMyQuery(props) {
this.setState({
loading: true,
data: null,
});
this.queryHandle = client.watchQuery({
query: getQuery(props),
});
this.queryHandle.onResult((error, data) => {
if (error) {
throw error;
}
this.setState({
data,
loading: false,
});
});
},
componentWillUnmount() {
this.queryHandle.stop();
},
render() {
return <Component {...this.props} {...this.state}/>;
}
});
} |
See now this makes more sense. In my mind I envision this playing out something like:
From there you use ActionCreators to as the public API to update Client State / Server State. Whats the API look for calling a mutation in this world? Are we still using |
No right now you call Ideally the React container for Apollo will also let you do the regular Redux mapStateToProps, so you can just use one container to get a graphql query, and your client-side data. |
Dont you think encapsulating I like that container can mapState, much like a Meteor Still not entirely sold on the update api right now |
Yeah, the mutation API is the weakest part at the moment. Once we start thinking about optimistic UI and how data refetching after mutations works, it will probably get better. One that that would be great is if mutations and redux actions could be fused together, so you could update your server and client in one step.
Great! Yeah I think not needing to stack up two containers will be great. I think having a one-stop-shop for data is going to be really cool. |
Awesome! I'm going to start using Apollo for an internal tool. Will probably have my own opinions about some of the API design once im in the thick of it. Ill report back on the issues any ideas I have |
Dude amazing! |
Gonna close this issue, but I listed it in the README as a good conversation to look at: 6de2414 |
@stubailo I'm attempting to query the current user's User data on the root export class App extends Component {
render() {
return (
<div>
<Navbar { ...this.props.data.user } />
{ React.cloneElement(this.props.children, { user: this.props.data.user }) }
<TimelineContainer />
</div>
);
}
}
const ViewerQuery = gql`
query userQuery($personId: String) {
user(personId: $personId) {
firstName
lastName
personId
username
}
}
`;
export default graphql(ViewerQuery, {
options: ownProps => ({
variables: {
personId: 2
}
})
})(withApollo(connect()(App))); I don't like using What is the recommended approach in a case such as this? A coworker recommended I use the same query in the children since Apollo should see that it's got all the necessary data and therefore not send our another GraphQL request. Thanks in advance! |
@AndrewHenderson If your children mount before the data is completely fetched, the children will fire their own queries. Is there a reason you don't want to stop the children from rendering while the data hasn't loaded yet? Do they have something useful to display without data? If not, you can check |
I have a couple questions about the Apollo Client store implementation
I see in the example that we have one reducer called
apollo
. What state does this represent? The current query? The thing with ambitious projects like this is I feel that we should be thinking long term. Having all state under one reducer seems like a bad idea and hard to manage going forward. Imagine mapping state to props over a huge reducer whos shape may be nested etc.you may end up with something like
Is there a way we can represent each query has a slice of our state. Where we somehow generate multiple reducers to manage domain state from different queries?
Sorry if this is naive in approach, just going off the docs at this point!
The text was updated successfully, but these errors were encountered: