Apollo 中的自动 UI 更新问题:`updateQuery` 不能与 `subscribeToMore` 一起正常工作

Posted

技术标签:

【中文标题】Apollo 中的自动 UI 更新问题:`updateQuery` 不能与 `subscribeToMore` 一起正常工作【英文标题】:Issue with automatic UI updates in Apollo: `updateQuery` not working properly with `subscribeToMore` 【发布时间】:2017-07-18 11:46:57 【问题描述】:

我正在为我的聊天应用程序使用 GraphQL 订阅,但我遇到了 UI 更新问题。

这是我正在使用的查询和突变:

const createMessage = gql`
    mutation createMessage($text: String!, $sentById: ID!) 
        createMessage(text: $text, sentById: $sentById) 
            id
            text
        
    
`

const allMessages = gql`
    query allMessages 
        allMessages 
            id
            text
            createdAt
            sentBy 
                name
                location 
                    latitude
                    longitude
                
            
        
    
`

然后,在导出时,我像这样包装我的 Chat 组件:

export default graphql(createMessage, name : 'createMessageMutation')(
  graphql(allMessages, name: 'allMessagesQuery')(Chat)
)

我在componentDidMount订阅allMessagesQuery

componentDidMount() 

  // Subscribe to `CREATED`-mutations
  this.createMessageSubscription = this.props.allMessagesQuery.subscribeToMore(
    document: gql`
        subscription 
            Message(filter: 
                mutation_in: [CREATED]
            ) 
                node 
                    id
                    text
                    createdAt
                    sentBy 
                        name
                    
                
            
        
    `,
    updateQuery: (previousState, subscriptionData) => 
      console.log('Chat - received subscription: ', previousState, subscriptionData)
      const newMessage = subscriptionData.data.Message.node
      const messages = previousState.allMessages.concat([newMessage])
      console.log('Chat - new messages: ', messages.length, messages) // prints the correct array with the new message!!
      return 
        allMessages: messages
      
    ,
    onError: (err) => console.error(err),
  )


通过聊天发送消息后,订阅成功触发,我看到两个日志语句也包含预期的数据。所以messages的内容在updateQuery内肯定是正确的!

但是,UI 不会自动更新,事实上,之前显示的所有消息都会消失。

我的render 方法如下所示:

render() 
  console.log('Chat - render: ', this.props.allMessagesQuery)
  return (
    <div className='Chat'>
      <ChatMessages
        messages=this.props.allMessagesQuery.allMessages || []
      />
      <ChatInput
        message=this.state.message
        onTextInput=(message) => this.setState(message)
        onResetText=() => this.setState(message: '')
        onSend=this._onSend
      />
    </div>
  )

render 中的日志记录语句显示,最初,this.props.allMessagesQuery 具有数组 allMessages,因此在初始加载后一切正常。

收到订阅后,allMessagesthis.props.allMessagesQuery 中消失,这就是为什么给ChatMessages 一个空数组而没有渲染的原因。

订阅触发前

订阅触发后

【问题讨论】:

【参考方案1】:

我又翻了一遍文档才发现,这是我的错误!

Apollo docs 的这一部分帮助我解决了这个问题:

请记住:您需要确保在需要对结果进行规范化的每个查询中选择 ID。

因此,将id 字段添加到我的查询和订阅的返回负载中确实有帮助。

const allMessages = gql`
    query allMessages 
        allMessages 
            id
            text
            createdAt
            sentBy 
                id
                name
                location 
                    id
                    latitude
                    longitude
                
            
        
    
`

subscription 
    Message(filter: 
        mutation_in: [CREATED]
    ) 
         node 
            id
            text
            createdAt
            sentBy 
                id
                name
            
        
    

【讨论】:

【参考方案2】:

在您的更新查询中,previousState 是保存在 react apollo 存储中的 allMessages 查询。

要更新商店,您必须进行深层复制或使用一些帮助程序,例如reacts immutability helper。 apollo 开发者页面提供了一些关于如何正确更新商店的信息update Query。

在你的情况下,它可能看起来像这样

...
updateQuery: (previousState,  subscriptionData ) => 
  const newMessage = subscriptionData.data.Message.node;
  return update(previousState, 
    allMessages:  $push: [newMessage] ,
  );

...

【讨论】:

以上是关于Apollo 中的自动 UI 更新问题:`updateQuery` 不能与 `subscribeToMore` 一起正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Apollo 客户端中的乐观响应与更新?

Apollo 客户端 writeQuery 更新存储,但 UI 组件仅在第二次函数调用后更新

在 Apollo-client 中,writeQuery 会更新缓存,但 UI 组件不会重新渲染

重新获取后 apollo graphql UI 未更新

Apollo Client/GraphQL 实时更新 UI [重复]

当我们从 Mongo 控制台更新集合时,Meteor Apollo 没有更新 UI