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

Issue with automatic UI updates in Apollo: `updateQuery` not working properly with `subscribeToMore`

我正在为我的聊天应用程序使用 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一个空数组,什么都没有渲染。

订阅触发前

订阅触发后

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

要更新商店,您必须制作深拷贝或使用一些帮助程序,例如reacts immutability helper. The apollo developers page gives some information about how to update the store correctly update Query.

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

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

我又翻了一遍文档才弄明白,这是我的一个错误!

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

Remember: You'll need to ensure that you select IDs in every query where you need the results to be normalized.

因此,将 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
            }
        }
    }
}