将YouGlish API代码转为React应用:点击按钮无响应问题排查
解决React中YouGlish Widget无法加载的问题
你的代码存在几个核心问题,导致点击按钮后无法正常渲染视频界面,以下是针对性的修复方案:
问题分析
- 组件渲染逻辑错误:直接调用
YouglishWidget()函数不会将返回的DOM元素插入React的渲染树,React要求组件通过条件渲染方式加载。 - 脚本重复加载:每次点击按钮都会插入新的
widget.js脚本,引发重复初始化冲突。 - 回调函数作用域问题:
onYouglishAPIReady未绑定到全局,YouGlish脚本加载后无法触发;onCaptionConsumed无法访问内部定义的widget实例。 - 状态管理不当:普通变量无法在React组件生命周期中保持状态,会导致闭包失效。
修复代码
1. 父组件(按钮控制逻辑)
用React状态控制Youglish组件的显示/隐藏,替代直接调用函数:
import { useState } from 'react'; import YouglishWidget from './YouglishWidget'; function YourParentComponent() { const [showYouglish, setShowYouglish] = useState(false); return ( <div id="buttons"> <button id="youglish" onClick={() => setShowYouglish(true)} style={{ display: showYouglish ? 'none' : 'block'}} > YouGlish </button> {/* 条件渲染Youglish组件 */} {showYouglish && <YouglishWidget />} </div> ); }
2. YouglishWidget组件(核心修复)
使用React Hooks处理脚本加载、API初始化和状态管理:
import { useEffect, useRef } from 'react'; export default function YouglishWidget() { // 用useRef保存widget实例和状态,避免闭包问题 const widgetRef = useRef(null); const viewsRef = useRef(0); const curTrackRef = useRef(0); const totalTracksRef = useRef(0); useEffect(() => { // 检查YouGlish API是否已加载,避免重复插入脚本 if (window.YG) { initWidget(); return; } // 动态加载Youglish脚本 const tag = document.createElement('script'); tag.src = "https://youglish.com/public/emb/widget.js"; // 脚本加载完成后初始化组件 tag.onload = initWidget; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); // 组件卸载时清理全局回调 return () => { if (window.onYouglishAPIReady) { delete window.onYouglishAPIReady; } }; }, []); // 初始化Youglish Widget function initWidget() { const widget = new YG.Widget("widget-1", { width: 640, components: 9, events: { onFetchDone: onFetchDone, onVideoChange: onVideoChange, // 传入widget实例解决作用域问题 onCaptionConsumed: () => onCaptionConsumed(widget), }, }); widget.fetch("courage", "english"); widgetRef.current = widget; } function onFetchDone(event) { if (event.totalResult === 0) { alert("No result found"); } else { totalTracksRef.current = event.totalResult; } } function onVideoChange(event) { curTrackRef.current = event.trackNumber; viewsRef.current = 0; } function onCaptionConsumed(widget) { if (++viewsRef.current < 3) { widget.replay(); } else if (curTrackRef.current < totalTracksRef.current) { widget.next(); } } return <div id="widget-1"></div>; }
关键修复点说明
- 条件渲染组件:通过
useState控制组件的挂载/卸载,符合React的渲染机制。 - 脚本加载优化:检查全局
window.YG避免重复加载脚本,提升性能并防止冲突。 - 作用域修复:用
useRef保存状态和实例,解决闭包导致的变量失效问题;将widget实例直接传入回调函数。 - 生命周期管理:使用
useEffect处理脚本加载和清理,确保组件卸载时不会遗留全局变量。
内容的提问来源于stack exchange,提问作者cagdasyilmaz
相关产品推荐
相关产品推荐

