如何在 React 应用程序中处理订阅
How to approach subscriptions in React app
我需要一些帮助来弄清楚订阅和实时更新的一般方法是什么。我有一个 React Native 应用程序,并使用 Apollo 和 Graphcool 服务作为后端。
在某些情况下,查看应用程序的用户会收到推送通知,告知内容已更改。当然,屏幕数据也应该得到更新。订阅是这项工作的明显候选人,我基本上让它工作了。
我有这样的订阅,它本身就很好用(用于在 google 地图上定位玩家头像)。
subscription PlayerMap($gameId: ID) {
Game(filter: { mutation_in: [CREATED, UPDATED], node: { id: $gameId } }) {
node {
players {
id
character {
id
name
}
user {
id
latitude
longitude
}
}
}
}
}
然后有一个不同的应用程序屏幕执行突变 createPlayer
以及来自 Apollo(为简单起见)的 refetchQueries
,它运行此查询以更新内容。
query GameCharacters($gameId: ID!) {
Game(id: $gameId) {
players {
id
character {
id
name
}
}
}
}
现在完成后,订阅查询(在另一个屏幕上仍处于活动状态)也会更新,但由于某种原因,整个 Game
节点在 data
对象中丢失。
为了处理订阅,我有一个这样的组件。
class Subscriber extends Component<void, Props, void> {
componentDidMount() {
this.subscribe()
}
componentWillReceiveProps({ data, shouldResubscribe }) {
if (this.unsubscribe) {
if (shouldResubscribe && shouldResubscribe(data, this.props.data) !== true) {
return
}
this.unsubscribe()
}
this.subscribe()
}
subscribe() {
const { data, query, variables } = this.props
this.unsubscribe = data.subscribeToMore({
document: query,
variables,
})
}
unsubscribe: ?Function = null
render() {
return this.props.children(this.props.data)
}
}
然后我可以像这样简单地将它与渲染道具模式一起使用。
const OrgMapScreen = ({ gameId, data: initialData }: Props) => (
<Subscriber
data={initialData}
query={OrgMapSubscription}
variables={{ gameId }}
shouldResubscribe={(nextData, prevData) => nextData.Game !== prevData.Game}
>
{({ Game }) => {
const markers = Game.players.map(makePlayerMarker)
return <MapScreen mapProps={{ markers }} />
}}
</Subscriber>
)
我很困惑为什么会这样。有没有一些推荐的方法来处理这样的事情?也许我也应该为 GameCharacters
设置另一个订阅而不是 refetchQueries
?
如果我不得不猜测(不是 Apollo 专家),我猜这与您 subscribeToMore
中错误的 document
输入有关(看起来您正在使用查询而不是订阅作为参数?)或 subscribeToMore
中缺少 updateQuery
,returns 更新数据。
在我们的例子中,我们有一个 orderChanged
订阅来监听 Order
上的变化。当我们收到更新时,我们想用更新后的订单替换 orders
查询中的订单。我们在 subscribeToMore
中的 updateQuery
函数中这样做(对于拼写错误,这不是精确的复制粘贴):
componentDidMount() {
this.props.data.subscribeToMore({
document: OrderSubscription,
variables: {
range: [0, 25]
},
updateQuery: (prev, { subscriptionData, }) => {
// If no subscription data is passed, just return the previous
// result from the initial `orders` query
if (!subscriptionData.data) return prev
// get the data for the updated order from the subscription
const updatedOrder = subscriptionData.data.orderChanged
// find the index of the updated order from within the existing
// array of orders from the `orders` query
const existingOrderIndex = prev.orders.findIndex(order => (order.id === updatedOrder.id))
// guard for missing data
if (existingOrderIndex) {
// replace the old order with the updated order
prev[existingOrderIndex] = updatedOrder
// return orders with new, updated data
return prev
}
return prev
},
})
}
如果你能为我提供一个最小可行的回购协议,我很乐意与你一起完成它。上周我花了很多时间处理订阅:)
我需要一些帮助来弄清楚订阅和实时更新的一般方法是什么。我有一个 React Native 应用程序,并使用 Apollo 和 Graphcool 服务作为后端。
在某些情况下,查看应用程序的用户会收到推送通知,告知内容已更改。当然,屏幕数据也应该得到更新。订阅是这项工作的明显候选人,我基本上让它工作了。
我有这样的订阅,它本身就很好用(用于在 google 地图上定位玩家头像)。
subscription PlayerMap($gameId: ID) {
Game(filter: { mutation_in: [CREATED, UPDATED], node: { id: $gameId } }) {
node {
players {
id
character {
id
name
}
user {
id
latitude
longitude
}
}
}
}
}
然后有一个不同的应用程序屏幕执行突变 createPlayer
以及来自 Apollo(为简单起见)的 refetchQueries
,它运行此查询以更新内容。
query GameCharacters($gameId: ID!) {
Game(id: $gameId) {
players {
id
character {
id
name
}
}
}
}
现在完成后,订阅查询(在另一个屏幕上仍处于活动状态)也会更新,但由于某种原因,整个 Game
节点在 data
对象中丢失。
为了处理订阅,我有一个这样的组件。
class Subscriber extends Component<void, Props, void> {
componentDidMount() {
this.subscribe()
}
componentWillReceiveProps({ data, shouldResubscribe }) {
if (this.unsubscribe) {
if (shouldResubscribe && shouldResubscribe(data, this.props.data) !== true) {
return
}
this.unsubscribe()
}
this.subscribe()
}
subscribe() {
const { data, query, variables } = this.props
this.unsubscribe = data.subscribeToMore({
document: query,
variables,
})
}
unsubscribe: ?Function = null
render() {
return this.props.children(this.props.data)
}
}
然后我可以像这样简单地将它与渲染道具模式一起使用。
const OrgMapScreen = ({ gameId, data: initialData }: Props) => (
<Subscriber
data={initialData}
query={OrgMapSubscription}
variables={{ gameId }}
shouldResubscribe={(nextData, prevData) => nextData.Game !== prevData.Game}
>
{({ Game }) => {
const markers = Game.players.map(makePlayerMarker)
return <MapScreen mapProps={{ markers }} />
}}
</Subscriber>
)
我很困惑为什么会这样。有没有一些推荐的方法来处理这样的事情?也许我也应该为 GameCharacters
设置另一个订阅而不是 refetchQueries
?
如果我不得不猜测(不是 Apollo 专家),我猜这与您 subscribeToMore
中错误的 document
输入有关(看起来您正在使用查询而不是订阅作为参数?)或 subscribeToMore
中缺少 updateQuery
,returns 更新数据。
在我们的例子中,我们有一个 orderChanged
订阅来监听 Order
上的变化。当我们收到更新时,我们想用更新后的订单替换 orders
查询中的订单。我们在 subscribeToMore
中的 updateQuery
函数中这样做(对于拼写错误,这不是精确的复制粘贴):
componentDidMount() {
this.props.data.subscribeToMore({
document: OrderSubscription,
variables: {
range: [0, 25]
},
updateQuery: (prev, { subscriptionData, }) => {
// If no subscription data is passed, just return the previous
// result from the initial `orders` query
if (!subscriptionData.data) return prev
// get the data for the updated order from the subscription
const updatedOrder = subscriptionData.data.orderChanged
// find the index of the updated order from within the existing
// array of orders from the `orders` query
const existingOrderIndex = prev.orders.findIndex(order => (order.id === updatedOrder.id))
// guard for missing data
if (existingOrderIndex) {
// replace the old order with the updated order
prev[existingOrderIndex] = updatedOrder
// return orders with new, updated data
return prev
}
return prev
},
})
}
如果你能为我提供一个最小可行的回购协议,我很乐意与你一起完成它。上周我花了很多时间处理订阅:)