Because the commands prop is an array, its value is always deemed to be changing when either (a) the default value is used or (b) the consumer does not memoize their commands value. The various callbacks and effects depending on commands get re-run on every change, including unrelated changes (e.g. transcript).
This was resulting in a race condition when multiple instances of SpeechRecognition were being rendered. While the transcript was being updated, the new transcripts would cause commands to "change" and re-run the dependent effects, including the one that subscribes and unsubscribes SpeechRecognition to RecognitionManager. As RecognitionManager updates its subscribers of transcript changes, they unsubscribe and resubscribe with a different ID. When having to iterate through many subscribers, the manager would sometimes find that a subscriber no longer existed due to unsubscribing with that old ID after a previous transcript change.
The solution is to stop subscribers from caring about changes to commands. matchCommands, the direct dependent of commands, now accesses it via a mutable ref, just getting whatever the latest value is.
I'm not sure how I feel about this - it feels like a misuse of hooks. Besides hashing commands and comparing that with its old value, this is the best I can come up with for now.
Because the
commands
prop is an array, its value is always deemed to be changing when either (a) the default value is used or (b) the consumer does not memoize theircommands
value. The various callbacks and effects depending oncommands
get re-run on every change, including unrelated changes (e.g. transcript).This was resulting in a race condition when multiple instances of
SpeechRecognition
were being rendered. While the transcript was being updated, the new transcripts would causecommands
to "change" and re-run the dependent effects, including the one that subscribes and unsubscribesSpeechRecognition
toRecognitionManager
. AsRecognitionManager
updates its subscribers of transcript changes, they unsubscribe and resubscribe with a different ID. When having to iterate through many subscribers, the manager would sometimes find that a subscriber no longer existed due to unsubscribing with that old ID after a previous transcript change.The solution is to stop subscribers from caring about changes to
commands
.matchCommands
, the direct dependent ofcommands
, now accesses it via a mutable ref, just getting whatever the latest value is.I'm not sure how I feel about this - it feels like a misuse of hooks. Besides hashing
commands
and comparing that with its old value, this is the best I can come up with for now.