AWS使用TypeScript和Hook扩展GraphQL操作 – 第4部分 [Subscriptions]
接下来是订阅。这是GraphQL和AppSync的一个有趣功能。利用实时数据的能力确实可以为您的应用带来一些不错的用户体验。我倾向于谨慎使用它,但它对小列表和即时反馈非常有帮助。
我们以前的帖子已经构建了一组相当不错的抽象,我们也可以在这里使用它们。因此,我将在此时开始粘贴结果并将其分解成碎片。拿一杯咖啡,我们就可以了。
编码
这是我们完成的自定义钩子:
type ConfigType<VariableType extends {}> = { query: string; key: string; variables?: VariableType; }; export const useSubscriptionByItself = < ItemType extends { id: string }, VariablesType extends {} = {} >({ config, itemData, }: { config?: ConfigType<VariablesType>; itemData?: ItemType; } = {}) => { const (item, update) = React.useState<ItemType | undefined>(itemData); React.useEffect(() => { let unsubscribe; if (config) { const { query, key, variables } = config; const subscription = API.graphql(graphqlOperation(query, variables)); if (subscription instanceof Observable) { const sub = subscription.subscribe({ next: payload => { try { const { value: { data: { (key): item }, }, }: { value: { data: { (key: string): ItemType } }; } = payload; update(item); } catch (error) { console.error( `${error.message} - Check the key property: the current value is ${key}` ); } }, }); unsubscribe = () => { sub.unsubscribe(); }; } } return unsubscribe; }, (JSON.stringify(config))); return (item); };
这里有很多,但我们的用例很简单。我们的订阅将处理一个项目。这可能就像订阅创建的新博客一样简单,例如:
const (item) = useSubscription<postFragment>({ config: { key: 'onCreatePost', query: onCreatePost, }, });
我们还可以在更新时传递一些变量来订阅注释:
const (comment) = useSubscriptionByItself< commentFragment, onUpdateCommentSubscriptionVariables >({ itemData: comment, config: { key: 'onUpdateComment', query: onUpdateComment, variables: { id, }, }, });
关键是我们能够采用的样板
const subscription = API.graphql(graphqlOperation(query, variables));
并将其提取为可以重复使用的东西,以及倾向于AWS Amplify如何以强类型方式返回数据来处理所有内容的惯例。
让我们从顶部开始,看看发生了什么。
输入配置
type ConfigType<VariableType extends {}> = { query: string; key: string; variables?: VariableType; }; export const useSubscription = < ItemType extends { id: string }, VariablesType extends {} = {} >({ config, itemData, }: { config?: ConfigType<VariablesType>; itemData?: ItemType; } = {}) => {
我们来看一下类型参数(尖括号之间的东西)。这需要一些解释,因为我开始假设一个约定。该 ItemType
表示我们将在钩子中返回并操作的对象。该 extends { id: string }
意味着无论我们传入什么对象,它都必须具有类型的id string
作为财产。这很有用,因为我们想要一个对象的唯一标识符。该 itemData
用于我们想要初始化我们的状态的情况。
请注意,我正在利用片段来提供我们可以使用的单个类型对象。一旦创建,放大 codegen
工具将为您的片段创建类型,然后您可以像我们在此示例中那样使用它们。您可以在此处了解有关片段以及如何在GraphQL中使用它们的更多信息。
第二 VariableType
将成为一个对象,表示我们将传递给我们的订阅的任何变量 graphqlOperation
。这在下面的类型声明中进一步使用 ConfigType
。这表示保留订阅的配置 query
, variables
和 key
我们将用它来建立我们的订阅。我们会回来的 key
稍后。
国家
const (item, update) = React.useState<ItemType | undefined>(itemData);
这非常简单。我们用的是 ItemType
我们传入的参数键入 useState
功能。这可能是未定义的,所以我们也注意到了。如果我们最初通过 itemData
,我们也使用它来建立将跟踪我们正在使用的订阅的状态。
效果
这是真正的肉。
React.useEffect(() => { let unsubscribe; if (config) { const { query, key, variables } = config; const subscription = API.graphql(graphqlOperation(query, variables)); if (subscription instanceof Observable) { const sub = subscription.subscribe({ next: payload => { try { const { value: { data: { (key): item }, }, }: { value: { data: { (key: string): ItemType } }; } = payload; update(item); } catch (error) { console.error( `${error.message} - Check the key property: the current value is ${key}` ); } }, }); unsubscribe = () => { sub.unsubscribe(); }; } } return unsubscribe; }, (JSON.stringify(config)));
首先,我们将建立一个订阅,所以为了效果,我们需要在完成后清理它。我们声明一个变量,它将保存从效果返回时要运行的函数。
接下来,我们将检查配置是否存在,因为它是可选的。我们对组件进行解构,并使用它们来构建我们的订阅。下一行很重要:
const subscription = API.graphql(graphqlOperation(query, variables)); if (subscription instanceof Observable) { ...
该 API.graphql
呼叫实际上返回 Observable | Promise<>
– 这意味着结果将是一个或另一个。为了获得我们期望的自动完成帮助(并阻止TypeScript对我们大喊大叫),我们需要使用类型保护来执行所谓的“类型缩小”。我们这样做是通过使用 instanceof
用于检查类型是否为的关键字 Observable
。我添加了 @types/zen-observable
包(yarn add -D @types/zen-observable
)提供类型。
订阅
const sub = subscription.subscribe({ next: payload => { try { const { value: { data: { (key): item }, }, }: { value: { data: { (key: string): ItemType } }; } = payload; update(item); } catch (error) { console.error( `${error.message} - Check the key property: the current value is ${key}` ); } }, }); unsubscribe = () => { sub.unsubscribe(); };
我们的订阅是从graphql调用返回的,所以现在我们需要订阅它。这是使用所谓的可观察的。上次我检查时,Amplify使用zen-observable库进行订阅实现。 Observable通过将它们作为流返回来操作值,因此您可以通过提供回调来监听流的更新 – 在这种情况下, next
。我们的 next
回调需要一个 payload
(这将是流中下一个事件的值)然后我们对此值进行一些解构以获取我们想要的基础数据。 Amplify遵循在订阅中返回数据的约定,因此我们可以使用它来确保我们的解构是正确的。
const { value: { data: { (key): item }, }, }: { value: { data: { (key: string): ItemType } }; } = payload;
我们用的是 key
我们之前谈过,以及 ItemType
我们传入的类型,从嵌套对象创建一个类型和正确的解构(以。的形式) value.data(key)
)。处理完这些数据后,我们就会使用 update
来自我们的方法 useState
挂钩以保持我们的状态,如果出现任何问题,我们会注销错误。
回调后,我们为我们分配一个小箭头函数 unsubscribe
如果卸载了使用挂钩的组件,则该变量将执行取消订阅的工作。
(JSON.stringify(config));
我们的 useEffect
hook接受一个依赖项(一个对象),所以我们只是 stringify
它确保如果它以任何方式改变,我们的钩子将再次运行,我们可以重新建立适当的订阅。
最后一行只返回保持状态的数据,因此我们可以从调用者中使用它。
return (item);
点评
从本质上讲,这只是现有Amplify工具的一个盘点器。但对于TypeScript项目,它可以为您提供帮助,以确保您的应用程序正在按预期执行。在我看来,不错的副产品是API表面更完整,同时抽象掉了公共位。提取这些东西并避免使用它通常是一种很好的做法 useEffect
直接在您的组件中。这只是朝这个方向迈出的一小步。
如果有人对此方法有任何反馈,请将其留在评论中。我经常使用它,只在TypeScript项目中,我希望它可以帮助某人。你也可以在twitter @mwarger上ping我。
…
但是等等,如果您需要订阅许多活动怎么办?那是下一个 – 跟着我发布时得到通知