I am passing getResponse()
function in the useEffect with userInput
as dependency array. Everytime a user sends input this function is triggered and I get new directline object created everytime. Problem I am facing is that I am creating bot everytime I send a request to bot. How can I create an object only once on initial render and then use the object to connect with bot.
Please help!
Here's the botframework documentation I am following
Here's the code I wrote to communicate with bot in ReactJS.
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;
You should move the creation of the DirectLine client
object, as well as subscribing to activity
and connectionStatus
, out of the function or configure the useEffect() hook to run only once. The client
object should be created once and then referenced, as needed. Check out this SO post that discusses how to implement these options.
Additionally, because you already subscribe to activity
and connectionStatus
, these should be allowed to run independently so they can function as expected. When the connectionStatus
changes, the switch statement will detect this. As the different cases are met, you could assign the status thru useState()
and detect the changes to state thru the useEffect()
hook.