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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 14:25:24