问题描述
我的用例是:
我有一个使用GraphQl查询获取的注释列表.当用户编写新评论时,它会使用GraphQL突变提交.然后,我正在使用updateQueries将新评论附加到列表中.
在UI中,我想突出显示新创建的评论.我尝试在mutationResult中的新注释中添加属性isNew: true,但是Apollo在将其保存到商店之前删除了该属性(我认为这是因为GQL查询中没有要求isNew字段)./p>
有什么方法可以实现这一目标?
推荐答案
取决于"新创建的对象"是什么意思.如果它是基于身份验证的应用程序与可以登录的用户,则可以将评论的create_date与某些用户的日期进行比较.如果用户不被迫创建帐户,则可以将这些信息存储在本地存储或cookie中(当他/她上次访问网站时).
另一方面,如果您考虑评论列表的实时更新,我建议您查看 graphql-subscriptions .它通过使用pub-sub机制为您提供了用户界面的反应性.简单用例 - 每当将新评论添加到帖子中时,每个用户/查看器都会通知有关此评论,可以将评论附加到评论列表中,并以您想要的方式突出显示.
为了实现这一目标,您可以创建一个称为newCommentAdded的subscription,客户端将订阅哪个,每次创建新评论时,应用程序的server侧都会通知(publish> publish ).
这种情况的简单实现看起来像
const Subscription = new GraphQLObjectType({ name: 'Subscription', fields: { newCommentAdded: { type: Comment, // this would be your GraphQLObject type for Comment resolve: (root, args, context) => { return root.comment; } } } }); // then create graphql schema with use of above defined subscription const graphQLSchema = new GraphQLSchema({ query: Query, // your query object mutation: Mutation, // your mutation object subscription: Subscription });
上面的部分仅是graphql-js部分,但是有必要创建使用PubSub机制的SubscriptionManager.
import { SubscriptionManager, PubSub } from 'graphql-subscriptions'; const pubSub = new PubSub(); const subscriptionManagerOptions = { schema: graphQLSchema, setupFunctions: { newCommentAdded: (options, args) => { newCommentAdded: { filter: ( payload ) => { // return true -> means that the subscrition will be published to the client side in every single case you call the 'publish' method // here you can provide some conditions when to publish the result, like IDs of currently logged in user to whom you would publish the newly created comment return true; } } }, pubsub: pubSub }); const subscriptionManager = new SubscriptionManager(subscriptionManagerOptions); export { subscriptionManager, pubSub };
最终步骤是在必要时通过上述SubscriptionManager实例到客户端新创建的评论.您可以在突变方法中创建新评论,或者在需要的任何地方
做到这一点// here newComment is your comment instance subscriptionManager.publish( 'newCommentAdded', { comment: newComment } );
为了通过使用websockets制造pub-sub机制,有必要与主服务器一起创建此类服务器.您可以使用 subscriptions-transport-ws subscriptions-transport-ws module.
这种解决方案的最大优点是它在您的应用程序中提供了反应性(应用于下面的评论列表等的实时更改等).我希望这对您的用例可能是一个不错的选择.
其他推荐答案
我可以看到这是多种方法.您是对的,Apollo会剥离ISNEW值,因为它不是您的模式的一部分,也不在查询选择集中列出.我喜欢将Apollo管理的服务器数据和前端应用程序状态分开,这些状态可以使用Redux/Flux,或者简单地将其用于组件的状态.
.Apollo为您提供了提供自己的Redux商店的选项.您可以允许Apollo管理其数据获取逻辑,然后在其旁边管理自己的前端状态.这是一篇文章讨论如何做到这一点: http://dev.apollodata.com/react/redux.html .
如果您使用的是React,则可以使用组件生命周挂钩来检测新注释何时出现.这可能有点黑客,但是您可以使用componentWillReceiveProps将新的注释列表与旧评论列表进行比较,确定是新的,存储在组件状态下,然后在一段时间后将它们无效使用setTimeout.
componentWillReceiveProps(newProps) { // Compute a diff. const oldCommentIds = new Set(this.props.data.allComments.map(comment => comment.id)); const nextCommentIds = new Set(newProps.data.allComments.map(comment => comment.id)); const newCommentIds = new Set( [...nextCommentIds].filter(commentId => !oldCommentIds.has(commentId)) ); this.setState({ newCommentIds }); // invalidate after 1 second const that = this; setTimeout(() => { that.setState({ newCommentIds: new Set() }) }, 1000); } // Then somewhere in your render function have something like this. render() { ... { this.props.data.allComments.map(comment => { const isNew = this.state.newCommentIds.has(comment.id); return <CommentComponent isNew={isNew} comment={comment} /> }) } ... }
上面的代码就在袖口上,因此您可能需要稍微播放一点.希望这会有所帮助:)
问题描述
My use case is the following:
I have a list of comments that I fetch using a GraphQL query. When the user writes a new comment, it gets submitted using a GraphQL mutation. Then I'm using updateQueries to append the new comment to the list.
In the UI, I want to highlight the newly created comments. I tried to add a property isNew: true on the new comment in mutationResult, but Apollo removes the property before saving it to the store (I assume that's because the isNew field isn't requested in the gql query).
Is there any way to achieve this?
推荐答案
Depends on what do you mean by "newly created objects". If it is authentication based application with users that can login, you can compare the create_date of comment with some last_online date of user. If the user is not forced to create an account, you can store such an information in local storage or cookies (when he/she last time visited the website).
On the other hand, if you think about real-time update of comments list, I would recommend you take a look at graphql-subscriptions with use of websockets. It provides you with reactivity in your user interface with use of pub-sub mechanism. Simple use case - whenever new comment is added to a post, every user/viewer is notified about that, the comment can be appended to the comments list and highlighted in a way you want it.
In order to achieve this, you could create a subscription called newCommentAdded, which client would subscribe to and every time a new comment is being created, the server side of the application would notify (publish) about that.
Simple implementation of such a case could look like that
const Subscription = new GraphQLObjectType({ name: 'Subscription', fields: { newCommentAdded: { type: Comment, // this would be your GraphQLObject type for Comment resolve: (root, args, context) => { return root.comment; } } } }); // then create graphql schema with use of above defined subscription const graphQLSchema = new GraphQLSchema({ query: Query, // your query object mutation: Mutation, // your mutation object subscription: Subscription });
The above part is only the graphql-js part, however it is necessary to create a SubscriptionManager which uses the PubSub mechanism.
import { SubscriptionManager, PubSub } from 'graphql-subscriptions'; const pubSub = new PubSub(); const subscriptionManagerOptions = { schema: graphQLSchema, setupFunctions: { newCommentAdded: (options, args) => { newCommentAdded: { filter: ( payload ) => { // return true -> means that the subscrition will be published to the client side in every single case you call the 'publish' method // here you can provide some conditions when to publish the result, like IDs of currently logged in user to whom you would publish the newly created comment return true; } } }, pubsub: pubSub }); const subscriptionManager = new SubscriptionManager(subscriptionManagerOptions); export { subscriptionManager, pubSub };
And the final step is to publish newly created comment to the client side when it is necessary, via above created SubscriptionManager instance. You could do that in the mutation method creating new comment, or wherever you need
// here newComment is your comment instance subscriptionManager.publish( 'newCommentAdded', { comment: newComment } );
In order to make the pub-sub mechanism with use of websockets, it is necessary to create such a server alongside your main server. You can use the subscriptions-transport-ws module.
The biggest advantage of such a solution is that it provides reactivity in your application (real-time changes applied to comments list below post etc.). I hope that this might be a good choice for your use case.
其他推荐答案
I could see this being done a couple of ways. You are right that Apollo will strip the isNew value because it is not a part of your schema and is not listed in the queries selection set. I like to separate the concerns of the server data that is managed by apollo and the front-end application state that lends itself to using redux/flux or even more simply by managing it in your component's state.
Apollo gives you the option to supply your own redux store. You can allow apollo to manage its data fetching logic and then manage your own front-end state alongside it. Here is a write up discussing how you can do this: http://dev.apollodata.com/react/redux.html.
If you are using React, you might be able to use component lifecycle hooks to detect when new comments appear. This might be a bit of a hack but you could use componentWillReceiveProps to compare the new list of comments with the old list of comments, identify which are new, store that in the component state, and then invalidate them after a period of time using setTimeout.
componentWillReceiveProps(newProps) { // Compute a diff. const oldCommentIds = new Set(this.props.data.allComments.map(comment => comment.id)); const nextCommentIds = new Set(newProps.data.allComments.map(comment => comment.id)); const newCommentIds = new Set( [...nextCommentIds].filter(commentId => !oldCommentIds.has(commentId)) ); this.setState({ newCommentIds }); // invalidate after 1 second const that = this; setTimeout(() => { that.setState({ newCommentIds: new Set() }) }, 1000); } // Then somewhere in your render function have something like this. render() { ... { this.props.data.allComments.map(comment => { const isNew = this.state.newCommentIds.has(comment.id); return <CommentComponent isNew={isNew} comment={comment} /> }) } ... }
The code above was right off the cuff so you might need to play around a bit. Hope this helps :)