I'm trying to implement React-Speech-Recognition and search functionality within one and the same text input field in order to give users also the possibility to insert their search term by speaking instead of just typing it via keyboard.
The search functionality works so far in the case of entering search terms via keyboard.
However, as soon as I insert "transcript" into the input element as
"<input value={(inputSearch, transcript)}"
and into the Link element as
"<Link to={/search/${(inputSearch, transcript)}
}>"
the search functionality doesn't work any more in the case of entering search terms via keyboard. The input via keyboard isn't renderd anymore...
How can I implement both functionalities, namely rendering/searching terms by speaking and typing it via keyboard?
Here's the code so far:
import React, { useState } from "react";
import "../Header.css";
import MenuSharpIcon from "@mui/icons-material/MenuSharp";
import SearchSharpIcon from "@mui/icons-material/SearchSharp";
import MicSharpIcon from "@mui/icons-material/MicSharp";
import VideoCallOutlinedIcon from "@mui/icons-material/VideoCallOutlined";
import NotificationsNoneSharpIcon from "@mui/icons-material/NotificationsNoneSharp";
import { Avatar } from "@mui/material";
import { Link } from "react-router-dom";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
function Header() {
const [inputSearch, setInputSearch] = useState("");
const handleClick = (e) => {
if (inputSearch.trim().length < 1) {
e.preventDefault();
}
setInputSearch("");
};
const {
transcript,
listening,
resetTranscript,
browserSupportsSpeechRecognition,
} = useSpeechRecognition();
if (!browserSupportsSpeechRecognition) {
return <span>Browser doesn't support speech recognition.</span>;
}
return (
<div className="header">
<div className="headerLeft">
<MenuSharpIcon />
<Link to="/">
<img
className="headerLogo"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/YouTube_Logo_2017.svg/1024px-YouTube_Logo_2017.svg.png?20220605194644"
alt="YouTube Logo"
onClick={(e) => setInputSearch("")}
/>
</Link>
</div>
<div className="headerInput">
<input
onChange={(e) => setInputSearch(e.target.value)}
value={inputSearch}
placeholder="Search"
type="text"
/>
<Link to={`/search/${inputSearch}`}>
<SearchSharpIcon
className="headerInputSearchIcon"
onClick={handleClick}
/>
</Link>
<MicSharpIcon
className="headerInputMicIcon"
onClick={SpeechRecognition.startListening}
/>
</div>
<div className="headerRight">
<div className="headerRight1" data-tooltip="Create">
<VideoCallOutlinedIcon className="headerRightIcon" />
</div>
<div className="headerRight2" data-tooltip="Notifications">
<NotificationsNoneSharpIcon className="headerRightIcon" />
</div>
<div className="headerRight3">
<Avatar className="headerRightIcon">ET</Avatar>
</div>
</div>
</div>
);
}
export default Header;
Any hints much appreciated!
I finally solved the problem after reconsidering it many times and splitting it into single logical steps. I also refactored the code as shown below.
import React, { useState, useEffect } from "react";
import "../Header.css";
import MenuSharpIcon from "@mui/icons-material/MenuSharp";
import SearchSharpIcon from "@mui/icons-material/SearchSharp";
import MicSharpIcon from "@mui/icons-material/MicSharp";
import VideoCallOutlinedIcon from "@mui/icons-material/VideoCallOutlined";
import NotificationsNoneSharpIcon from "@mui/icons-material/NotificationsNoneSharp";
import { Avatar } from "@mui/material";
import { Link } from "react-router-dom";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
function Header() {
const [inputSearch, setInputSearch] = useState("");
const handleClick = (e) => {
if (inputSearch.trim().length < 1) {
e.preventDefault();
}
setInputSearch("");
};
const { transcript, listening, browserSupportsSpeechRecognition } =
useSpeechRecognition();
useEffect(() => {
setInputSearch();
}, [listening]);
if (!browserSupportsSpeechRecognition) {
return <span>Browser doesn't support speech recognition.</span>;
}
return (
<div className="header">
<div className="headerLeft">
<MenuSharpIcon />
<Link to="/">
<img
className="headerLogo"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/YouTube_Logo_2017.svg/1024px-YouTube_Logo_2017.svg.png?20220605194644"
alt="YouTube Logo"
onClick={(e) => setInputSearch("")}
/>
</Link>
</div>
<div className="headerInput">
{!listening ? (
<input
onChange={(e) => setInputSearch(e.target.value)}
value={inputSearch}
placeholder="Search"
type="text"
/>
) : (
<input value={transcript} />
)}
<Link to={`/search/${inputSearch || transcript}`}>
<SearchSharpIcon
className="headerInputSearchIcon"
onClick={handleClick}
/>
</Link>
<MicSharpIcon
className="headerInputMicIcon"
onClick={SpeechRecognition.startListening}
/>
</div>
<div className="headerRight">
<div className="headerRight1" data-tooltip="Create">
<VideoCallOutlinedIcon className="headerRightIcon" />
</div>
<div className="headerRight2" data-tooltip="Notifications">
<NotificationsNoneSharpIcon className="headerRightIcon" />
</div>
<div className="headerRight3">
<Avatar className="headerRightIcon">ET</Avatar>
</div>
</div>
</div>
);
}
export default Header;
The main takeaway is that I had to update the DOM by using the useEffect Hook and an inline if-else with conditional operator in order to distinguish when to render which input.