You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 07:24:00