React使用Bot Framework Direct Line JS客户端如何避免重复创建DirectLine对象
实现方案
核心改造点
- 用
useRef存储DirectLine实例:useRef的current属性不会随组件重渲染重置,适合存储需要全局复用的长连接实例 - 初始化逻辑移至空依赖useEffect:保证DirectLine实例仅在组件首次渲染时创建一次
- 修复状态闭包问题:修改消息更新逻辑为函数式更新,避免订阅回调中获取到旧的状态值
- 移除多余触发逻辑:删除依赖userInput的useEffect,无需每次用户输入都重建连接
具体修改步骤
步骤1:导入useRef并创建实例存储对象
// 原有导入基础上增加useRef import React, { useEffect, useState, useRef } from "react"; // 组件内部替换原有var client; const clientRef = useRef(null);
步骤2:新增空依赖useEffect初始化DirectLine
把原来getResponse里的初始化逻辑全部放到这个useEffect里,仅执行一次:
useEffect(() => { // 避免重复初始化 if (clientRef.current) return; const initDirectLine = async () => { try { // 获取令牌 const res = await fetch( "https://directline.botframework.com/v3/directline/tokens/generate", { method: "POST", headers: { Authorization: `Bearer TOKEN`, }, } ); const { token } = await res.json(); // 初始化DirectLine实例存入ref clientRef.current = new DirectLine({ domain: "https://directline.botframework.com/v3/directline", token: token, }); // 订阅消息 const activitySubscription = clientRef.current.activity$.subscribe((activity) => { if (activity.inputHint === "acceptingInput") { // 用函数式更新避免闭包问题 setMessages(prev => [...prev, { messageKey: activity.text, isbotmessage: true }]); } }); // 订阅连接状态 const statusSubscription = clientRef.current.connectionStatus$ .subscribe(connectionStatus => { switch(connectionStatus) { case ConnectionStatus.Uninitialized: case ConnectionStatus.Connecting: case ConnectionStatus.Online: setLoaded(true); break; case ConnectionStatus.ExpiredToken: // 令牌过期时可在这里重新获取令牌调用reconnect,无需重建实例 // const newToken = await 重新请求令牌; // clientRef.current.reconnect(newToken); break; case ConnectionStatus.FailedToConnect: case ConnectionStatus.Ended: setLoaded(false); break; } console.log("connection status ", connectionStatus) }); // 组件卸载时取消订阅,清理连接 return () => { activitySubscription.unsubscribe(); statusSubscription.unsubscribe(); clientRef.current?.end(); } } catch (e) { console.log("bot error", e) } }; initDirectLine(); }, []);
步骤3:修改sendRequest用ref中的实例发消息
const sendRequest = () => { console.log("request sent"); clientRef.current ?.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) ); };
步骤4:删除冗余逻辑
- 删除原来依赖
userInput的useEffect - 删除原来的
getResponse、onResponse函数(逻辑已合并到初始化useEffect中) - 原有触发消息发送的逻辑(handleKeyPress、handleClick)不需要改动,直接调用sendRequest即可
额外优化说明
- 原来的
userInput状态实际没有被用到,可以直接删除相关赋值逻辑 - 令牌过期不需要重建DirectLine实例,直接调用实例的
reconnect方法传入新令牌即可复用连接
内容的提问来源于stack exchange,提问作者NinjaTS
相关产品推荐
相关产品推荐

