如何最好地从 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)
})
正如您从下图中看到的那样,由于请求很慢,我正在使用 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)
})