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

Lee*_*fin 4 amazon-web-services amazon-dynamodb reactjs graphql aws-amplify

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

我的 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;

Run Code Online (Sandbox Code Playgroud)

问题是,subscription只有当我将新数据插入到 DynamoDB 之外时,DynamoDB 中的新数据插入才有效,function App(){...}正如您在上面看到的那样。那么,当newData订阅回调中收到 时next: (newData),如何在App组件中显示新数据呢?

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

PS Amplify GraphQL API 文档示例代码(如果您看到“订阅”部分)还显示订阅代码与导入处于同一级别。

Log*_*nRx 5

我们在项目中使用 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'
  }
}))
Run Code Online (Sandbox Code Playgroud)

我们使用助手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)
    }
  })
}
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

从那里您可以将其提取到组件内或项目内的辅助函数中,如上面所示