实时字段
客户端状态和服务器状态之间的一个关键区别是,随着客户端状态随时间推移而变化,这些变化需要反映在您的 UI 中。为了解决这个问题,Relay 解析器支持将解析器标记为 @live
。预期实时解析器将返回一个 LiveState
形状的对象,其中包含允许 Relay read()
当前值以及 subscribe()
值变化的方法。
随着这个值随时间推移而变化,Relay 将自动重新计算任何依赖于此字段的 派生字段(包括如果更改级联时的传递依赖关系),并且还会有效地触发任何组件/订阅者的更新,这些组件/订阅者已读取由于此更改而更新的字段。
@live
- Docblock
要将解析器标记为实时解析器,请将 @live
docblock 标签添加到解析器定义中。例如
import type { LiveState } from 'relay-runtime';
/**
* @RelayResolver Query.counter: Int
* @live
*/
export function counter(): LiveState<number> {
return {
read: () => store.getState().counter,
subscribe: (callback) => {
return store.subscribe(callback);
},
};
}
字段解析器和强模型解析器(将 ID 映射到模型)都可以被注释为 @live
。
LiveState 类型
实时解析器的返回类型被称为 LiveState
。它在概念上类似于可观察对象或信号(如果您熟悉这些概念)。与可观察对象不同的是,当 LiveState
通知其订阅者更新时,它不会包含新值。相反,订阅者(Relay)应调用 read()
来获取新值。
虽然支持过度通知(当读取的值实际上没有更改时,订阅通知),但出于性能原因,建议 LiveState 值提供者在通知 Relay 更改之前确认该值确实已更改。
LiveState 的类型定义如下
export type LiveState<T> = {
/**
* Returns the current value of the live state.
*/
read(): T,
/**
* Subscribes to changes in the live state. The state provider should
* call the callback when the value of the live state changes.
*/
subscribe(cb: () => void): () => void,
};
创建 LiveState 对象
在大多数情况下,您将希望定义一个辅助函数,该函数读取您的响应式数据存储并返回一个 LiveState
对象。例如,对于 Redux 存储,您可以编写一个包装器,它为给定选择器公开 LiveState
type Selector<T> = (state: State) => T;
function selectorAsLiveState<T>(selector: Selector<T>): LiveState<T> {
let currentValue = selector(store.getState());
return {
read: () => currentValue,
subscribe: (cb) => {
return store.subscribe(() => {
const newValue = selector(store.getState());
if (newValue === currentValue) {
return;
}
currentValue = newValue;
cb();
});
return unsubscribe;
},
};
}
使用此辅助函数的实时解析器可能如下所示
/**
* @RelayResolver Query.counter: Int
* @live
*/
export function counter(): LiveState<number> {
return selectorAsLiveState(getCounter);
}
function getCounter(state) {
return state.counter;
}
批处理
当数据层中的状态发生更改时,可能一个更改会导致通知许多 @live
解析器订阅有关更新的信息。默认情况下,这些更新中的每一个都将要求 Relay 执行工作以确定哪些组件需要更新。这会导致执行大量重复的工作。
如果可能,建议您对 @live
解析器进行批量更新。这可以通过将您的状态更新包装在 RelayStore
实例上的 batchLiveStateUpdates()
调用中来完成。
与 Redux 存储的典型用法可能如下所示
const store = createStore(reducer);
const originalDispatch = store.dispatch;
function wrapped(action) {
relayStore.batchLiveStateUpdates(() => {
originalDispatch(action);
})
}
store.dispatch = wrapped;