如何在 React 应用中处理订阅
Posted
技术标签:
【中文标题】如何在 React 应用中处理订阅【英文标题】:How to approach subscriptions in React app 【发布时间】:2018-03-27 02:01:51 【问题描述】:我需要一些帮助来弄清楚订阅和实时更新的一般方法是什么。我有一个 React Native 应用程序并使用 Apollo 和 Graphcool 服务作为后端。
在某些情况下,用户查看应用程序时会收到推送通知,说明发生了一些变化。自然,屏幕数据也应该得到更新。订阅是该工作的明显候选者,我基本上得到了它的工作。
我有一个这样的订阅,它本身就可以正常工作(用于在谷歌地图上定位玩家头像)。
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
现在完成后,订阅查询(在另一个屏幕上仍处于活动状态)也会更新,但由于某种原因,data
对象中缺少整个 Game
节点。
为了处理订阅,我有一个这样的组件。
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
?
【问题讨论】:
【参考方案1】:如果我不得不猜测(不是 Apollo 专家),我想这与您的 subscribeToMore
中的错误 document
输入有关(看起来您使用的是查询而不是订阅参数?)或subscribeToMore
中缺少updateQuery
,返回更新后的数据。
在我们的例子中,我们有一个 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
,
)
如果您能给我提供一个最小可行的回购协议,我很乐意与您一起完成它。我上周大部分时间都在处理订阅问题:)
【讨论】:
是的,事实证明updateQuery
有点必要。我不知道为什么我的印象是 Apollo 会自行抓取结果并根据类型和 dataIdFromObject
更新存储。那好吧。不得不将immutable-helper
用于像我这样的深层嵌套结构,但它现在似乎正在工作。谢谢以上是关于如何在 React 应用中处理订阅的主要内容,如果未能解决你的问题,请参考以下文章
如何在 React 中为上传和订阅设置 Apollo 客户端?
如何在 React 和 Flask 中检测过期的 PayPal 订阅?