问题
I'm using GraphQL subscriptions for my chat application, but I have an issue with the UI updates.
Here are the query and the mutation I'm using:
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
}
}
}
}
`
Then, when exporting, I'm wrapping my Chat
component like so:
export default graphql(createMessage, {name : 'createMessageMutation'})(
graphql(allMessages, {name: 'allMessagesQuery'})(Chat)
)
I'm subscribing to the allMessagesQuery
in componentDidMount
:
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),
})
}
After I sent the message through the chat, the subscription is triggered successfully and I see the two logging statements that also contain the expected data. So the contents of messages
are definitely correct within updateQuery
!
However, the UI doesn't update automatically, in fact, all the previously displayed messages disappear.
My render
method looks as follows:
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>
)
}
The logging statement in render
shows that initially, this.props.allMessagesQuery
has the array allMessages
, so everything works after the initial loading.
After the subscription is received, allMessages
disappears from this.props.allMessagesQuery
which is why an empty array is given to ChatMessages
and nothing is rendered.
Before subscription is triggered ✅
After subscription is triggered ❌
回答1:
I figured it out after digging through the docs one more time, it was a mistake on my end!
This part from the Apollo docs helped me solve the issue:
Remember: You'll need to ensure that you select IDs in every query where you need the results to be normalized.
So, adding the id
fields to the returned payload of my queries and subscriptions actually helped.
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:
In your update Query the previousState is the allMessages query that is saved in the react apollo store.
To update the store you have to make a deep copy or use some helper e.g. reacts immutability helper. The apollo developers page gives some information about how to update the store correctly update Query.
In your case it could look like this
...
updateQuery: (previousState, { subscriptionData }) => {
const newMessage = subscriptionData.data.Message.node;
return update(previousState, {
allMessages: { $push: [newMessage] },
});
}
...
来源:https://stackoverflow.com/questions/42492943/issue-with-automatic-ui-updates-in-apollo-updatequery-not-working-properly-wi