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

只愿长相守 提交于 2019-12-24 12:24:26

问题


As you can see from the picture below, I'm rendering the popover using a react-relay QueryRenderer since the request is slow, and I do not want the rest of the page to wait for events to be fetched.

My problem is that in the navigation I have a button to show/hide the popover. That button should only be rendered when events has loaded, and the button also needs to show a count of how many events there is.

So my question is how to pass events data up from QueryRenderer (popover) to a parent component (toggle button)?

My first idea was to reuse my QueryRenderer for events and pass in dataFrom={'STORE_ONLY'}, to avoid a new HTTP request and use the cache instead, but unfortunately 'STORE_ONLY' is not an option... YET...

From looking at https://github.com/relay-tools/relay-hooks/issues/5 it seems like store-only will be supported by useQuery in the future, so is that the recommended solution to go about it, or how is the recommended way? Surely facebook, and many other applications, must have had this need frequently?


回答1:


You can achieve redux-like relay store with custom handlers and local schema.

I'll be guessing what your queries, components and fields might be named like so don't forget to change it to correct values

Somewhere in project's src folder create a file ClientState.client.graphql to extend your root query type with new field for client state:

// ClientState.client.graphql

type ClientState {
  showToggleButton: Boolean!
  eventsCount: Int
}

extend type Query {
  clientState: ClientState!
}

this will allow you to wrap Toggle button with fragment like this:

fragment ToggleButton_query on Query {
  clientState {
    showToggleButton
    eventsCount
  }
}

and spread this fragment in parent query (probably AppQuery)

Then in your second query, where you'll be fetching events, add @__clientField directive, to define custom handle for that field:

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

Create EventsDataHandler for handle eventsData:

// 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

Last thing to do is to assign custom (and default) handlers to environment and create init store values:

// 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)
})


来源:https://stackoverflow.com/questions/57617861/how-to-best-access-data-from-queryrenderer-in-a-parent-component-in-relay-modern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!