AWS Amplify in React,如何从订阅的侦听器呈现新数据

Posted

技术标签:

【中文标题】AWS Amplify in React,如何从订阅的侦听器呈现新数据【英文标题】:AWS Amplify in React, how can I render the new data from the subscribed listener 【发布时间】:2021-07-06 09:27:14 【问题描述】:

我正在开发一个 React 项目并使用 AWS Amplify 作为无服务器后端的工具,其中我有一个 DynamoDB 作为我在 AWS 云中的数据库。

我的 React 应用需要检测 DynamoDB 中的实时数据更新。我正在使用 Amplify 的 GraphQL API。

Amplify 生成的 GraphQL API 提供了一个subscriptions API,用于实时订阅数据更改。我认为这正是我需要的,所以我正在使用它:

import React, useEffect, useState, useRef from 'react';
import Amplify,  API, graphqlOperation  from 'aws-amplify';
import  onCreateData  from './graphql/subscriptions';

// subscribe to listen to new data creation in DynamoDB
const subscription = API.graphql(
    graphqlOperation(onCreateData)
).subscribe(
    next: (newData) => 
      // New data is received here outside 'App' functional component,
      // how can I render the data then?
      console.log(`new data: $JSON.stringify(newData)`);
      
    
);

// I can't put the above subscription code inside App component, I don't know why but it just doesn't work unless I move it outside the App functional component.

function App() 
  const [dataArray, setDataArray] = useState([]);
   ...

  useEffect(() => 
    fetchDataArray();
    showDataArray();
    return () => 
      //cleanup
    
  , [dataArra])

  return (<div>
    <canvas ref=canvasRef/>
  </div>)


export default App;

问题是 subscription 用于 DynamoDB 中的新数据插入仅在我将其放在 function App()... 之外时才有效,如上所示。那么,当订阅回调next: (newData)收到newData时,如何在App组件中显示新数据?

你可以在我的App 组件中看到我有状态变量dataArray,理想的解决方案是用newData 更新这个dataArray 状态,但我认为现在不可能。所以,总的来说,我想知道现在如何在我的App 组件中显示新数据?

附: Amplify GraphQL API doc sample code(如果您看到“订阅”部分)也显示订阅代码与导入处于同一级别。

【问题讨论】:

学习一些教程? react FC(你的App是FC,可以转成类组件)需要像useSubscription-apollographql.com/docs/react/data/subscriptions这样的hook形式 我想知道如何订阅我的功能组件,而不是类组件。有没有可能。 您是否尝试在 App 中使用 useEffect 挂钩添加它? 【参考方案1】:

我们在我们的项目中使用 amplify,我们的一个示例是下面的简单聊天功能:

interface ChatMessagesProps 
  internal?: boolean
  convoId: string


export const ChatMessages: FC<ChatMessagesProps> = ( internal, convoId ) => 
  const classes = useStyles()
  const 
    appState:  user 
   = useAppState()

  let [messages, setMessages] = useState<any>([])
  let [currentConversationId, setConversationId] = useState<string>(convoId)
  const listRef = useRef<VariableSizeProps>()

  let subscription = useRef<ISubscriptionObject | null>(null)
  let updateSubscription = useRef<ISubscriptionObject | null>(null)

  const addNewMessage = ( onCreateMessage ) => 
    console.log(onCreateMessage)
    // from here we can do whatever we want with the data within the context of this component
  

  const messageUpdated = ( onUpdateMessage ) => 
    console.log(onUpdateMessage)
    // from here we can do whatever we want with the data within the context of this component
  

  const getConversationDetails = async () => 
    subscription.current = graphQLSubscription(
      onCreateMessage,
       conversationId: currentConversationId ,
      addNewMessage
    )

    updateSubscription.current = graphQLSubscription(
      onUpdateMessage,
       conversationId: currentConversationId ,
      messageUpdated
    )
  

  useEffect(() => 
    if (currentConversationId) 
      getConversationDetails()
    
    return () => 
      subscription?.current?.unsubscribe()
      updateSubscription?.current?.unsubscribe()
    
  , [currentConversationId])

  return (
    <div className=classes.root>
      <MessageList listRef=listRef messages=messages internal=internal />
      <MessageInput userId=user?.id || '' internal=internal conversationId=currentConversationId />
    </div>
  )


const useStyles = makeStyles(() => (
  root: 
    width: '100%',
    height: '100%',
    backgroundColor: 'white',
    position: 'relative'
  
))

我们使用助手graphQLSubscription

export const graphQLSubscription = (subscription: string, options: any, callback: (d: any) => void) => 
  return API.graphql(graphqlOperation(subscription, options)).subscribe(
    next: ( value:  data, errors  ) => 
      console.log(errors)
      callback(data)
    
  )

对于您的示例代码,您只想将订阅存储在 ref 中,以便能够在卸载时取消订阅

import React, useEffect, useState, useRef from 'react';
import Amplify,  API, graphqlOperation  from 'aws-amplify';
import  onCreateData  from './graphql/subscriptions';

function App() 
  const [dataArray, setDataArray] = useState([]);
  const subscriptionRef = useRef()
   ...

  useEffect(() => 
    fetchDataArray();
    showDataArray();

    subscriptionRef.current = API.graphql(
      graphqlOperation(onCreateData)
    ).subscribe(
      next: (newData) => 
        // New data is received here outside 'App' functional component,
        // how can I render the data then?
        console.log(`new data: $JSON.stringify(newData)`);
      
    );

    return () => 
      //cleanup
      subscriptionRef.current.unsubscribe()
    
  , [dataArra])

  return (<div>
    <canvas ref=canvasRef/>
  </div>)


export default App;

您可以从那里将其提取到组件内或您的项目中的辅助函数中

【讨论】:

以上是关于AWS Amplify in React,如何从订阅的侦听器呈现新数据的主要内容,如果未能解决你的问题,请参考以下文章

aws-amplify-react 和 @aws-amplify/ui-react 有啥区别?

如何在带有 Typescript 项目的 React Native 中使用 AWS Amplify?

如何使用 aws amplify 保持用户登录 react-native

AWS Amplify Authenticator 和 React Native Navigation Container 如何在两者之间切换

AWS Amplify and React - 找不到模块:无法解析“@aws-amplify/analytics”

如何使用 aws-amplify 验证 node/express 中的 accessToken?