如何在 Relay Modern 的父组件中最好地访问来自 QueryRenderer 的数据?

Posted

技术标签:

【中文标题】如何在 Relay Modern 的父组件中最好地访问来自 QueryRenderer 的数据?【英文标题】:How to best access data from QueryRenderer in a parent component in Relay Modern? 【发布时间】:2019-12-28 06:44:59 【问题描述】:

从下图中可以看出,我使用 react-relay QueryRenderer 渲染弹出框,因为请求很慢,并且我不希望页面的其余部分等待获取事件。

我的问题是在导航中我有一个按钮来显示/隐藏弹出框。该按钮仅应在事件加载后呈现,并且该按钮还需要显示有多少事件的计数。

所以我的问题是如何将事件数据从 QueryRenderer(弹出窗口)传递到父组件(切换按钮)?

我的第一个想法是为事件重用我的 QueryRenderer 并传入 dataFrom='STORE_ONLY',以避免新的 HTTP 请求并改用缓存,但不幸的是,'STORE_ONLY' 不是一个选项......但是......

从https://github.com/relay-tools/relay-hooks/issues/5 看来,store-only 似乎将来会被useQuery 支持,那么这是推荐的解决方案,还是推荐的方法是什么? facebook 和许多其他应用程序肯定经常有这种需求吗?

【问题讨论】:

您实际上可以使用 Portals reactjs.org/docs/portals.html 从查询渲染器的子级渲染该按钮 谢谢@Boris 一个门户可以工作,但不是最佳的,但是谢谢......我所追求的是如何做一些类似于在 AppQueryRenderer 下的 MyEventsToggleButton 和 EventModal 组件中拥有片段的事情。问题只是我无法在 AppQueryRenderer(根渲染器)中获取事件,因为加载需要很长时间,因此会阻止该 AppQuery 中的其他数据。但是,您肯定应该能够在反应树的不同位置的组件中获得相同的状态片段吗?喜欢上下文甚至 redux,您可以在其中连接到感兴趣的状态? @defer 指令非常适合这个,遗憾的是它不受支持:/ github.com/facebook/relay/issues/2194 我将尝试描述如何在答案中实现类似 redux 的行为 谢谢。如果中继不能以优雅的方式处理这个问题,我会非常失望。应该是非常基础的。 【参考方案1】:

您可以使用自定义处理程序和本地架构实现类似 redux 的中继存储。

我会猜测您的查询、组件和字段可能会这样命名,所以不要忘记将其更改为正确的值

在项目的 src 文件夹中的某处创建一个文件 ClientState.client.graphql 以使用客户端状态的新字段扩展您的根查询类型:

// ClientState.client.graphql

type ClientState 
  showToggleButton: Boolean!
  eventsCount: Int


extend type Query 
  clientState: ClientState!

这将允许您用这样的片段包装切换按钮:

fragment ToggleButton_query on Query 
  clientState 
    showToggleButton
    eventsCount
  

并在父查询中传播这个片段(可能是AppQuery

然后在您将要获取事件的第二个查询中,添加 @__clientField 指令,为该字段定义自定义句柄:

query EventModal 
  events @__clientField(handle: "eventsData") 
    totalCount
  

为句柄eventsData创建EventsDataHandler

// EventsDataHandler.js

// update method will be called every time when field with `@__clientField(handle: "eventsData")` is fetched

const EventsDataHandler = 
  update (store, payload) 
    const record = store.get(payload.dataID)

    if (!record) 
      return
    

    // get "events" from record
    const events = record.getLinkedRecord(payload.fieldKey)

    // get events count and set client state values
    const eventsCount = events.getValue('totalCount')
    const clientState = store.getRoot().getLinkedRecord('clientState')

    clientState.setValue(eventsCount, 'eventsCount')
    clientState.setValue(true, 'showToggleButton')

    // link "events" to record, so the "events" field in EventModal is not undefined
    record.setLinkedRecord(events, payload.handleKey)
  


export default EventsDataHandler

最后要做的是将自定义(和默认)处理程序分配给环境并创建初始化存储值:

// environment.js

import  commitLocalUpdate, ConnectionHandler, Environment, RecordSource, Store, ViewerHandler  from 'relay-runtime'
import EventsDataHandler from './EventsDataHandler'

// ...

const handlerProvider = handle => 
  switch (handle) 
    case 'connection':
      return ConnectionHandler
    case 'viewer':
      return ViewerHandler
    case 'eventsData':
      return EventsDataHandler
    default:
      throw new Error(`Handler for $handle not found.`)
  


const environment = new Environment(
  network,
  store,
  handlerProvider
)

// set init client state values
commitLocalUpdate(environment, store => 
  const FIELD_KEY = 'clientState'
  const TYPENAME = 'ClientState'

  const dataID = `client:$FIELD_KEY`

  const record = store.create(dataID, TYPENAME)

  record.setValue(false, 'showToggleButton')

  // prevent relay from removing client state
  environment.retain(
    dataID,
    variables: ,
    node:  selections: [] 
  )

  store.getRoot().setLinkedRecord(record, FIELD_KEY)
)

【讨论】:

哇。谢谢楼主这么透彻的回答。我之前创建了一个赏金,所以我会让它运行几天,但是除非有人想出一个更简单的中继方式来做到这一点,否则我当然会接受你的答案并用赏金奖励它(和建议我的公司不要选择中继,因为这对于做一件微不足道的事情来说是很多代码)。再次,非常感谢! 我遇到了一个问题并得到了这个编译错误RelayFieldHandleTransform: Expected fields to have at most one "handle" property, got [object Object], [object Object]. 我猜是因为事件已经有一个指令,现在看起来像这样:@987654335 @ 如果我删除 @__clientField 它会再次编译。 哦,对了,当它是一个连接时,它有点难,所以可能在 totalCount 字段上使用该指令并稍微更改处理程序 (const eventsCount = record.getLinkedRecord(payload.fieldKey))。或者您可以在与@connection(handler: "customHandle" key: "connection_key") 的连接上设置自定义处理程序,但正确处理连接非常复杂。 github.com/facebook/relay/blob/master/packages/relay-runtime/…

以上是关于如何在 Relay Modern 的父组件中最好地访问来自 QueryRenderer 的数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Relay Modern 中取消订阅

如何在 TypeScript 中使用 Relay Modern(babel-plugin-relay 和 relay-compiler)?

如何使用 Relay Modern 突变进行文件上传?

在 Relay Modern 中处理身份验证

Relay Modern - 如何在没有 fragmentContainer 的情况下使用 fetchQuery

Relay Modern-啥是database.js,有必要吗?