ak1394 / react-native-tts

React Native Text-To-Speech library for Android and iOS
603 stars 154 forks source link

Events are fired multiple times on UWP. Intentional? #212

Open Tekbert opened 2 years ago

Tekbert commented 2 years ago

I made this component:

/**
 * @flow
 */

import React, { Component } from "react";
import {
    Platform,
    StyleSheet,
    Text,
    View,
    Button,
    FlatList,
    Slider,
    TextInput,
    Keyboard
} from "react-native";
import Tts from "react-native-tts";

type Props = {};
export default class App extends Component<Props> {
    state = {
        voices: [],
        ttsStatus: "initiliazing",
        selectedVoice: null,
        speechRate: 0.5,
        speechPitch: 1,
        text: "This is an example text"
    };

    constructor(props: Props | Readonly<Props>) {
        super(props);
        Tts.addEventListener("tts-start", event => {
            console.log("start", event)
            this.setState({ ttsStatus: "started" })
        }
        );
        Tts.addEventListener("tts-finish", event => {
            console.log("finish", event)
            this.setState({ ttsStatus: "finished" })
        }
        );
        Tts.addEventListener("tts-cancel", event => {
            console.log("cancel", event)
            this.setState({ ttsStatus: "cancelled" })
        }
        );
        Tts.setDefaultRate(this.state.speechRate);
        Tts.setDefaultPitch(this.state.speechPitch);
        Tts.getInitStatus().then(this.initTts);
    }

    initTts = async () => {
        const voices = await Tts.voices();
        const availableVoices = voices
            .filter(v => !v.networkConnectionRequired && !v.notInstalled)
            .map(v => {
                return { id: v.id, name: v.name, language: v.language };
            });
        let selectedVoice = null;
        if (voices && voices.length > 0) {
            selectedVoice = voices[0].id;
            try {
                await Tts.setDefaultLanguage(voices[0].language);
            } catch (err) {
                // My Samsung S9 has always this error: "Language is not supported"
                console.log(`setDefaultLanguage error `, err);
            }
            await Tts.setDefaultVoice(voices[0].id);
            this.setState({
                voices: availableVoices,
                selectedVoice,
                ttsStatus: "initialized"
            });
        } else {
            this.setState({ ttsStatus: "initialized" });
        }
    };

    readText = async () => {
        Tts.stop();
        Tts.speak(this.state.text);
    };

    setSpeechRate = async (rate: number) => {
        await Tts.setDefaultRate(rate);
        this.setState({ speechRate: rate });
    };

    setSpeechPitch = async (rate: number) => {
        await Tts.setDefaultPitch(rate);
        this.setState({ speechPitch: rate });
    };

    onVoicePress = async (voice: { language: string; id: string; }) => {
        try {
            await Tts.setDefaultLanguage(voice.language);
        } catch (err) {
            // My Samsung S9 has always this error: "Language is not supported"
            console.log(`setDefaultLanguage error `, err);
        }
        await Tts.setDefaultVoice(voice.id);
        this.setState({ selectedVoice: voice.id });
    };

    renderVoiceItem = ({ item }: any) => {
        return (
            <Button
                title={`${item.language} - ${item.name || item.id}`}
                color={this.state.selectedVoice === item.id ? undefined : "#969696"}
                onPress={() => this.onVoicePress(item)}
            />
        );
    };

    render() {
        return (
            <View >
                <Text >{`React Native TTS Example`}</Text>

                <Button title={`Read text`} onPress={this.readText} />

                <TextInput
                    multiline={true}
                    onChangeText={text => this.setState({ text })}
                    value={this.state.text}
                    onSubmitEditing={Keyboard.dismiss}
                />
            </View>
        );
    }
}

If i launch this as windows UWP, this causes the start/finish console logs to trigger multiple times after hitting the "Read text button". I assume it should only be called once? is there a way to fix this?

grafik

mberatsanli commented 1 year ago

you should add the event listeners in componentDidMount, then remove the listeners in componentWillUnmount

see https://github.com/ak1394/react-native-tts/pull/135

useEffect example


React.useEffect(() => {
    const ttsStartEvent = Tts.addEventListener("tts-start", event => {
            console.log("start", event);
   });

   return () => {
      ttsStartEvent.remove();
   };
}, []);