【问题标题】:Using Apollo, how can I distinguish newly created objects?使用 Apollo,我如何区分新创建的对象?
【发布时间】:2017-07-09 03:45:13
【问题描述】:

我的用例如下:

我有一个使用 GraphQL 查询获取的 cmets 列表。当用户编写新评论时,它会使用 GraphQL 突变提交。然后我使用updateQueries 将新评论附加到列表中。

在 UI 中,我想突出显示新创建的 cmets。我试图在mutationResult 中的新评论上添加属性isNew: true,但Apollo 在将其保存到商店之前删除了该属性(我认为这是因为在gql 查询中未请求isNew 字段)。

有什么办法可以做到吗?

【问题讨论】:

  • 评论发表的日期是否可用?
  • 我不认为使用日期是一个可靠的解决方案。你将如何检测评论是否是新的?您是否检查它是否是在最后 10 秒内创建的?还是在最后 100 毫秒内?每一个都有自己的问题。
  • "isNew" 在某个固定时间点添加到第一条评论。在此之前的每条评论都不再是新的。
  • 我应该把那个“固定时间点”保存在哪里?我不想使用全局变量。此外,该页面包含多个帖子,每个帖子都有自己的 cmets 列表。所以我更喜欢 CommentList 组件中包含的解决方案。

标签: javascript graphql apollo-client


【解决方案1】:

取决于“新创建的对象”是什么意思。如果是基于身份验证的应用程序,用户可以登录,您可以将评论的create_date 与一些用户的last_online 日期进行比较。如果不强制用户创建帐户,您可以将此类信息存储在本地存储或 cookie 中(当他/她上次访问该网站时)。


另一方面,如果您考虑实时更新 cmets 列表,我建议您使用 websockets 查看graphql-subscriptions。它使用pub-sub 机制为您提供用户界面的反应性。简单的用例 - 每当向帖子添加新评论时,都会通知每个用户/查看者,评论可以附加到 cmets 列表并以您想要的方式突出显示。

为了实现这一点,您可以创建一个名为newCommentAddedsubscription,客户端将订阅它,并且每次创建新评论时,应用程序的server 端会通知(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 实例将publish 新创建的评论发送到客户端。您可以在创建新评论的突变方法中执行此操作,或者在您需要的任何地方执行此操作

// here newComment is your comment instance
subscriptionManager.publish( 'newCommentAdded', { comment: newComment } );

为了使用websockets 来制作pub-sub 机制,有必要在主服务器旁边创建这样一个服务器。您可以使用subscriptions-transport-ws 模块。

这种解决方案的最大优势在于它在您的应用程序中提供了反应性(实时更改应用于帖子下方的 cmets 列表等)。我希望这对于您的用例来说可能是一个不错的选择。

【讨论】:

  • 感谢您抽出宝贵时间回答。我实际上对来自服务器的新 cmets 不感兴趣。我只关心当前用户正在写的cmets。
【解决方案2】:

我可以看到这是通过几种方式完成的。你是对的,Apollo 将去掉 isNew 值,因为它不是你的模式的一部分,也没有在查询选择集中列出。我喜欢将由 apollo 管理的服务器数据的关注点与适合使用 redux/flux 或者更简单地通过在组件的状态中管理它的前端应用程序状态分开。

Apollo 让您可以选择提供自己的 redux 商店。您可以允许 apollo 管理其数据获取逻辑,然后管理您自己的前端状态。这是一篇讨论如何做到这一点的文章:http://dev.apollodata.com/react/redux.html

如果您使用 React,您也许可以使用组件生命周期挂钩来检测新 cmets 何时出现。这可能有点小技巧,但您可以使用componentWillReceiveProps 将新的 cmets 列表与旧的 cmets 列表进行比较,确定哪些是新的,将其存储在组件状态中,然后在一段时间后使它们失效使用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} />
    })
  }
  ...
}

上面的代码是即兴的,所以你可能需要玩一下。希望这会有所帮助:)

【讨论】:

  • 感谢您的回答。我目前正在做与您的回答非常相似的事情:) 但我正在寻找一个不那么 hacky 的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-12
  • 1970-01-01
  • 2017-07-09
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多