- The Context
- React Mic and Recording
- The Problem: To Transcribe or Not to Transcribe?
- Key Considerations
- The Final Solution
- Conclusion
When developing the multifunctional AI assistant app, I encountered a common dilemma in React: should I use useState
or useRef
to manage a piece of logic? In this article, I’ll walk you through my decision process and explain why I opted for useRef
in a scenario involving user speech transcription.
The Context
The app has two key buttons: a play/stop toggle button and a reset button. Here’s the desired behavior:
- When the user presses the stop button, their speech is transcribed and displayed in a text box.
- When the reset button is pressed, the recording is cleared and not sent for transcription, since transcription costs both time and money.
However, both buttons stop the recording, so I needed a way to distinguish which button triggered the stop action. This would determine whether or not the transcription should occur.
React Mic and Recording
I used the react-mic library to handle speech recording. The library captures the user’s speech and provides it as an audio blob. Typically, you use the component like this:
<ReactMic
record={isRecording}
onStop={handleStop}
{...otherProps}
/>
When isRecording
is set to false
, the recording stops and handleStop
is triggered, receiving the audio blob as an argument.
const handleStop = (returnedBlob) => {
// Process the blob
}
Since both the stop and reset buttons stop the recording, I needed a way to determine if the transcription should happen. This led to the decision between useState
and useRef
.
The Problem: To Transcribe or Not to Transcribe?
I wanted to set a flag (shouldTranscribe
) that would:
- Trigger transcription only when the stop button is pressed.
- Prevent transcription when the reset button is clicked.
The core issue is that both buttons set the recording state to false
, which in turn triggers handleStop
. To avoid sending the recording for transcription when the reset button is pressed, I needed a flag that could differentiate between the two actions.
Key Considerations
-
Avoid Unnecessary Re-renders
useState
triggers a component re-render whenever its value is updated. This behavior is unnecessary here because changingshouldTranscribe
doesn’t affect the UI. It’s a purely logical decision: whether or not to send the recording for transcription. On the other hand,useRef
can hold a mutable value without triggering a re-render. This makes it perfect for managing non-UI-related logic like a flag that determines whether transcription should happen. -
Immediate Access to the Updated Value
useState
updates are asynchronous, meaning that if I useduseState
to toggleshouldTranscribe
, the new value wouldn’t be immediately available during the stop event handling. In contrast,useRef
provides immediate access to the updated value, which is essential for making synchronous decisions, like whether to transcribe the audio.
The Final Solution
Here’s the solution I implemented using useRef
:
const shouldTranscribeRef = useRef(false)
const handleStart = () => {
setIsRecording(true)
}
const handleStop = () => {
shouldTranscribeRef.current = true // Transcription will happen
setIsRecording(false)
}
const handleReset = () => {
shouldTranscribeRef.current = false // No transcription
setIsRecording(false)
// Other code omitted
}
const handleTranscribe = async (returnedBlob) => {
if (shouldTranscribeRef.current) {
// Send the blob for transcription
}
}
return (
<div>
<ReactMic
record={isRecording}
onStop={handleTranscribe}
{...otherProps}
/>
{isRecording ? (
<button onClick={handleStop}>
Stop
</button>
) : (
<button onClick={handleStart}>
Play
</button>
)}
<button onClick={handleReset}>
Send
</button>
</div>
)
This way, I managed to achieve the designed behaviour shown in The Context section.
Conclusion
In this case, using useRef
was a better fit than useState
because it:
- Avoided unnecessary re-renders.
- Provided immediate access to the updated value.
This pattern worked well in my AI assistant app, where I needed to manage non-UI logic efficiently. When deciding between useState
and useRef
, consider whether you need reactivity (in which case useState
is appropriate) or just an instance variable (where useRef
is better suited).
For more information, see React documentation on refs.