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

How to best access data from QueryRenderer in a parent component in Relay Modern?

正如您从下图中看到的那样,由于请求很慢,我正在使用 react-relay QueryRenderer 渲染弹出窗口,并且我不希望页面的其余部分等待获取事件。

我的问题是在导航中我有一个 show/hide 弹出窗口的按钮。该按钮仅应在加载事件时呈现,并且该按钮还需要显示有多少事件的计数。

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

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

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

您可以使用自定义处理程序和本地模式实现类似 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)
})