React Native中Voice组件多实例unique_key绑定异常问题
问题:React Native多实例VoiceRecorder组件回调始终指向最后一个实例
我基于@react-native-voice/voice开发了VoiceRecorder组件,需要在父组件中渲染多个独立实例,每个实例持有专属的Voice实例。我尝试克隆Voice对象并绑定unique_key标识,但无论触发哪个组件的语音识别,onSpeechError和onSpeechResults回调始终输出最后一个实例的unique_key(即2)。
现有VoiceRecorder组件代码
import React, { Component } from 'react'; import { Text, View, Button} from 'react-native'; import Voice from '@react-native-voice/voice'; var all_speech_instances = []; export default class VoiceRecorder extends Component { constructor(props) { super(props); // 尝试克隆Voice对象,为每个组件实例创建独立实例 this.SpeechInstance = Object.assign(Object.create(Object.getPrototypeOf(Voice)), Voice); this.SpeechInstance.unique_key = props.unique_key; let obj_this = this; this.SpeechInstance.onSpeechError = function(e) { obj_this.onSpeechError(e) }; this.SpeechInstance.onSpeechResults = function(e) { obj_this.onSpeechResults(e) }; // 验证实例存储情况 all_speech_instances.push(this.SpeechInstance); if(all_speech_instances.length > 1){ console.log('Instance 1 => unique_key='+all_speech_instances[0].unique_key); // 输出1 console.log('Instance 2 => unique_key='+all_speech_instances[1].unique_key); // 输出2 } } _startRecognizing = async () => { await this.SpeechInstance.start(); }; _stopRecognizing = async () => { await this.SpeechInstance.stop(); }; onSpeechError(e) { console.log('On speech error Key=' + this.SpeechInstance.unique_key); // 始终输出2 }; onSpeechResults(e){ console.log('On speech results Key=' + this.SpeechInstance.unique_key, e.value); // 始终输出2 }; componentWillUnmount() { this.SpeechInstance.removeAllListeners(); } render() { let obj_this = this; return ( <View style={{padding:10}}> <Text>unique_key: {this.SpeechInstance.unique_key}</Text> <View style={{padding: 5}}> <Button onPress={()=>{ obj_this._startRecognizing()}} title='Start Listening' /> </View> <View style={{padding: 5}}> <Button onPress={()=>{ obj_this._stopRecognizing()}} title='Stop Listening' /> </View> </View> ); } }
组件使用方式
import React from 'react'; import {View} from 'react-native'; import SpeechComponent from '../voice/SpeechComponent'; export default class VoiceTestScreen extends React.Component { render(){ return( <View> <SpeechComponent unique_key='1'/> <SpeechComponent unique_key='2'/> </View> ); } }
问题原因
@react-native-voice/voice的底层原生模块是全局单例,你通过Object.assign和Object.create克隆的只是JS层面对象壳,底层事件发射器仍然共享同一个全局实例。当第二个组件绑定回调时,会直接覆盖全局事件发射器上的回调函数,导致所有语音事件都会触发最后注册的那个回调,因此只会输出最后一个实例的unique_key。
修复方案
方案1:利用闭包绑定组件专属回调
不需要克隆Voice对象,直接使用全局Voice实例,但为每个组件注册带闭包的专属回调,并在卸载时移除,避免干扰其他实例:
import React, { Component } from 'react'; import { Text, View, Button} from 'react-native'; import Voice from '@react-native-voice/voice'; export default class VoiceRecorder extends Component { constructor(props) { super(props); this.unique_key = props.unique_key; // 为当前组件创建专属回调,通过闭包绑定unique_key this.errorHandler = (e) => this.onSpeechError(e); this.resultsHandler = (e) => this.onSpeechResults(e); Voice.onSpeechError(this.errorHandler); Voice.onSpeechResults(this.resultsHandler); } _startRecognizing = async () => { await Voice.start(); }; _stopRecognizing = async () => { await Voice.stop(); }; onSpeechError(e) { console.log('On speech error Key=' + this.unique_key); }; onSpeechResults(e){ console.log('On speech results Key=' + this.unique_key, e.value); }; componentWillUnmount() { // 必须移除当前组件的回调,避免内存泄漏和干扰其他实例 Voice.removeListener('speechError', this.errorHandler); Voice.removeListener('speechResults', this.resultsHandler); } render() { return ( <View style={{padding:10}}> <Text>unique_key: {this.unique_key}</Text> <View style={{padding: 5}}> <Button onPress={this._startRecognizing} title='Start Listening' /> </View> <View style={{padding: 5}}> <Button onPress={this._stopRecognizing} title='Stop Listening' /> </View> </View> ); } }
方案2:使用库的多实例构造(版本需支持)
部分新版本的@react-native-voice/voice已支持创建独立实例,可直接通过构造函数生成专属实例:
// 注意:需确认你的库版本是否支持此方式 import { Voice } from '@react-native-voice/voice'; export default class VoiceRecorder extends Component { constructor(props) { super(props); // 创建独立的Voice实例 this.SpeechInstance = new Voice(); this.SpeechInstance.unique_key = props.unique_key; // 绑定当前组件的回调 this.SpeechInstance.onSpeechError = (e) => this.onSpeechError(e); this.SpeechInstance.onSpeechResults = (e) => this.onSpeechResults(e); } // 后续的start/stop及回调逻辑保持原有写法即可 }
内容的提问来源于stack exchange,提问作者Sami
相关产品推荐
相关产品推荐

