如何在 Microsoft Bot Framework Directline JS 客户端(REACT JS)中每次都创建直线对象而不从服务器获取响应

How can I get response from the server without creating directline object everytime in Microsoft Bot Framework Direct Line JS Client (REACTJS)

我在 useEffect 中传递 getResponse() 函数,使用 userInput 作为依赖数组。每次用户发送输入时,都会触发此功能,我每次都会创建新的直线对象。我面临的问题是每次向机器人发送请求时我都在创建机器人。如何在初始渲染时仅创建一次对象,然后使用该对象与 bot 连接。

请帮忙!

Here's the botframework documentation I am following

这是我编写的用于在 ReactJS 中与 bot 通信的代码。

import React, { useEffect, useState } from "react";
import "./App.css"
import { DirectLine } from "botframework-directlinejs";
import { browserName, browserVersion, osName } from "react-device-detect";
import { ConnectionStatus } from 'botframework-directlinejs';
import Header from "./Components/Header";
import MainBody from "./Components/MainBody";
import BottomWrapper from "./Components/BottomWrapper";

function App() {
  useEffect(() => {
    const userIpDetails = fetchIp();
    setUserIp(userIpDetails);
  }, []);

  // to fetch user IP
  const fetchIp = async () => {
    const response = await fetch("https://api.ipify.org/?format=json");
    const ip = await response.json();
    const userIP = ip.ip;
    return userIP;
  };

  const [loaded, setLoaded] = useState(false);
  const [message, setMessage] = useState("");
  const [userInput, setUserInput] = useState([]);
  const [messages, setMessages] = useState([]);
  const [userIp, setUserIp] = useState("");

  var client;

  useEffect(() => {
    setLoaded(true);
    getResponse();
  }, [userInput]);

  // to get DirectLine Streaming Token
  const getDirectLineStreamingToken = async function () {
    const res = await fetch(
      https://directline.botframework.com/v3/directline/tokens/generate,
      {
        method: "POST",
        headers: {
          Authorization:
            `Bearer TOKEN`,
        },
      }
    );
    const { token } = await res.json();
    return token;
  };

  // send request via message box
  const sendRequest = () => {
    console.log("request sent");
    client
      ?.postActivity({
        from: {
          id: "my_id",
          name: "Software Company",
          avatarUrl:
            "https://demos.telerik.com/kendo-ui/content/chat/VacationBot.png",
        },
        type: "message",
        text: message,
        channelData: {
          iP: userIp,
          platform: `${osName}`,
          Browser: `${browserName} ${browserVersion}`,
        },
      })
      ?.subscribe(
        (id) => console.log("Posted activity, assigned ID ", id),
        (error) => console.log("Error posting activity", error)
      );
  };

  // receive response from server
  const getResponse = function () {
    getDirectLineStreamingToken().then(function (token) {
      client = new DirectLine({
        domain: "https://directline.botframework.com/v3/directline",
        token: token,
      });

      client.activity$.subscribe((activity) => {
        onResponse(activity);
      });

      client.connectionStatus$
      .subscribe(connectionStatus => {
          switch(connectionStatus) {
              case ConnectionStatus.Uninitialized:    // the status when the DirectLine object is first created/constructed
              case ConnectionStatus.Connecting:       // currently trying to connect to the conversation
              case ConnectionStatus.Online:           // successfully connected to the converstaion. Connection is healthy so far as we know.
              case ConnectionStatus.ExpiredToken:     // last operation errored out with an expired token. Your app should supply a new one.
              case ConnectionStatus.FailedToConnect:  // the initial attempt to connect to the conversation failed. No recovery possible.
              case ConnectionStatus.Ended:            // the bot ended the conversation
          }
          console.log("connection status ", connectionStatus)
      });
    })
    .catch((e) => console.log("bot error", e));;
  };

  // handle response received from server
  const onResponse = function (activity) {
    console.log("activity is ", activity);
    let receivedResponse = activity.text;
    if (activity.inputHint === "acceptingInput") {
      setMessages([
        ...messages,
        { messageKey: receivedResponse, isbotmessage: true },
      ]);
    }
  };

  const handleChange = function (event) {
    setMessage(event.target.value);
    console.log(event.target.value);
  };

  const handleKeyPress = function (event) {
    if (event.key === "Enter") {
      sendRequest();
      setMessages([...messages, { messageKey: message, isbotmessage: false }]);
      setUserInput([
        ...messages,
        { messageKey: userInput, isbotmessage: false },
      ]);
    }
  };

  const handleClick = function (event) {
    sendRequest();
    setMessages([...messages, { messageKey: message, isbotmessage: false }]);
    setUserInput([...messages, { messageKey: userInput, isbotmessage: false }]);
  };

  return loaded ? (
    <div className="App">
      <div className="chat-window">
        <Header />
        <MainBody messages={messages}/>
        <BottomWrapper message={message} handleChange={handleChange} handleKeyPress={handleKeyPress} handleClick={handleClick}/>
      </div>
    </div>
  ) : (
    <p>Loading...</p>
  );
}

export default App;

您应该将 DirectLine client 对象的创建以及对 activityconnectionStatus 的订阅移出函数 or 将 useEffect() 挂钩配置为 运行 一次。 client 对象应该创建一次,然后根据需要引用。查看讨论如何实现这些选项的 SO post

此外,因为您已经订阅了 activityconnectionStatus,所以应该允许它们独立 运行 以便它们可以按预期运行。当 connectionStatus 发生变化时,switch 语句将检测到这一点。当遇到不同的情况时,您可以通过 useState() 分配状态并通过 useEffect() 钩子检测状态的变化。