React项目中如何向HTTP客户端脚本传递URL变量
解决React组件向外部脚本传递自定义URL的问题
问题分析
你的React组件WebChatWidget能正确获取到用户自定义的serviceURL,但APIClient类中输出的URL却为undefined,核心问题有三点:
- APIClient构造函数逻辑错误:构造函数接收了
serviceURL参数,但未使用,反而尝试访问不存在的widget对象(widget仅在React组件中可用,无法在独立的APIClient类中直接调用)。 - 异步脚本加载时机问题:外部脚本是异步加载的,组件中直接调用方法时,脚本可能还未加载完成,导致
APIClient无法接收参数。 - 属性名拼写错误:构造函数中
console.log("Service URL:", this.serviceURL)写错了属性名,应为this.apiUrl。
解决方案
1. 修正APIClient类的构造函数
移除错误的widget.get调用,直接使用构造函数传入的serviceURL参数,并修正日志输出的属性名:
export class APIClient { constructor(serviceURL) { // 直接使用传入的serviceURL参数 this.apiUrl = serviceURL; this.apiKey = process.env.REACT_APP_OPENAI_API_KEY; // 修正属性名:this.apiUrl 而非 this.serviceURL console.log("Service URL:", this.apiUrl); } async sendMessage(message) { console.log('API-Key:' + this.apiKey); try { const apiBody = { "model": "gpt-3.5-turbo", "messages": [{ "role": "user", "content": "Can you act like you are the Chatbot of the University" + "of Heilbronn aka HHN and answer the following prompt and format the message with the respect to only use 100 tokens with the API call: " + message }], "max_tokens": 100, "temperature": 0, } console.log(JSON.stringify(apiBody)) const response = await fetch(this.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.apiKey, }, body: JSON.stringify(apiBody), }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${response.statusText}`); } const data = await response.json(); return data.choices[0].message.content; } catch (error) { console.error(error); return 'Sorry, something went wrong.'; } } }
2. 外部脚本暴露APIClient到全局作用域
确保打包后的外部脚本将APIClient挂载到window对象上,这样React组件就能在脚本加载完成后访问到它:
// 在APIClient类所在的脚本末尾添加 window.APIClient = APIClient;
3. 修正React组件的脚本加载逻辑
使用Promise等待所有外部脚本加载完成后,再实例化APIClient并传入serviceURL,同时避免重复加载脚本:
import * as React from "react"; import * as Scrivito from "scrivito"; import "./WebChatWidget.scss"; const WebChatWidget = ({ widget }) => { const serviceURL = widget.get("serviceURL"); console.log("Service URL Widget:", serviceURL); React.useEffect(() => { // 移除已存在的脚本,防止重复加载 const existingScripts = document.querySelectorAll( 'script[src="/static/js/main.14ec3828.js"], script[src="/static/js/787.4ed02641.chunk.js"]' ); existingScripts.forEach(script => script.remove()); // 创建加载脚本的Promise函数 const loadScript = (src) => { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.async = true; script.onload = () => resolve(); script.onerror = (err) => reject(err); document.body.appendChild(script); }); }; // 按顺序加载所有脚本,完成后实例化APIClient Promise.all([ loadScript("/static/js/main.14ec3828.js"), loadScript("/static/js/787.4ed02641.chunk.js") ]).then(() => { if (window.APIClient) { // 实例化APIClient并传入自定义URL const apiClient = new window.APIClient(serviceURL); // 可将apiClient存储到组件状态或全局,供聊天逻辑使用 console.log("APIClient initialized with URL:", serviceURL); } else { console.error("APIClient未在全局作用域中找到"); } }).catch(error => { console.error("脚本加载失败:", error); }); // 清理函数:组件卸载时移除脚本 return () => { const scripts = document.querySelectorAll( 'script[src="/static/js/main.14ec3828.js"], script[src="/static/js/787.4ed02641.chunk.js"]' ); scripts.forEach(script => script.remove()); }; }, [serviceURL]); return <div id="Chatbot" className="WebChatWidget" style={{ zIndex: 1000 }}></div>; }; Scrivito.provideComponent("WebChatWidget", WebChatWidget);
验证效果
完成上述修改后,重新运行项目:
- React组件会在
serviceURL变化时,重新加载脚本并实例化APIClient APIClient构造函数中的日志会正确输出用户自定义的URL- 聊天功能会使用传入的自定义URL发起请求
内容的提问来源于stack exchange,提问作者bayd
相关产品推荐
相关产品推荐

