I have a voice dictation custom hook, along with a separate custom hook that appends the dictation results to an object that stores "Note" values.
If the user clicks save too early, there are still Partial Results that I need to append right before an API call that saves the Note.
My code looks like this
function NoteDictation(props) {
const [
results,
partialResults,
error,
toggleRecognizing,
speechHasStarted,
] = useDictation();
const [note, setNote, saveNoteAPICall, updateNoteAPICall] = useNote({})
//Use Note is a custom hook that has that certain type of note's properties built in (they're medical notes, and we have a custom hook for each type of note).
function handleSavePress(){
if(partialSpeechResults){
//If the dictation software hasn't returned a final result,
//append the partialSpeechResults
setNote({...note, text: note.text + partialSpeechResults})
}
//SaveNote does not use the value set above if there are partial results.
saveNote()
}
return (
<View>
<NoteContents note={note} results={results} partialResults={partialResults} />
<Button onPress={handleSavePress> />
</View>
)
}
Problem is, the SaveNote is being called and is using the old state of the note...the setting of the state is not completing on time.
I can't seem to use a useEffect hook here to monitor the change, since I am calling the API to save the note right away and it's accessing the note state when saving.
What is the best way to handle this? Thank you.
Try the useEffect hook:
EDIT: Since it runs on first render you want to be sure the note object is not empty before saving
useEffect(() => {
if(Object.keys(note).length !== 0){
saveNote();
}
});
Given the updated code, you should be able to handle it very similarly to how I outlined in my original answer:
// will run on mount and whenever note changes
useEffect(() => {
// skip first run (what you check depends on your initial note value)
if (Object.keys(note).length) {
saveNoteAPICall()
}
}, [note])
function handleSavePress(){
if(partialSpeechResults){
// If the dictation software hasn't returned a final result,
// append the partialSpeechResults
// and let if fall into useEffect when note updates
setNote({...note, text: note.text + partialSpeechResults})
} else {
// run immediately if not partial
saveNoteAPICall()
}
}
The key difference is that you only call saveNote
inside your press handler if you don't have a partial result. That way you don't get incomplete saves. If you setNote
, it will drop into your useEffect
and save with the right value.
If this is a common pattern for handling these notes, it might make sense to move this logic into your useNote
hook.
Since you're using useState
for your note
value, you should be able to handle this with useEffect
. Values from useState
are immutable, so they work great as inputs for effects hooks. Move your call to saveNote()
outside of handleSavePress
and into useEffect
:
const [note, setNote] = useState({})
// ...Other misc code
// this will run on first execution,
// and then any time the value of note changes
useEffect(() => {
// skip first run
if (Object.keys(note).length) {
saveNote(note)
}
}, [note])
function handleSavePress(){
if (partialSpeechResults) {
// If the dictation software hasn't returned a final result,
// append the partialSpeechResults
setNote({ ...note, text: note.text + partialSpeechResults })
}
}
If, for some reason, your saveNote
function is defined inside this component, I would suggest moving it outside the component and passing it note
as an argument so you can be sure useEffect
will run only when you want it to. If there is some compelling reason why you need to define saveNote
inside the component, then you should define saveNote
with useCallback
and change your useEffect
function to key off changes to that:
const [note, setNote] = useState({})
// ...Other misc code
// this function will only change when note changes
const saveNote = useCallback(() => {
// whatever api call you need to run here
// that uses the note value in scope
}, [note])
// this will run on first execution,
// and then any time the value of note (and thus saveNote) changes
useEffect(() => {
// skip first run
if (Object.keys(note).length) {
saveNote()
}
}, [saveNote, note])
function handleSavePress(){
if (partialSpeechResults) {
// If the dictation software hasn't returned a final result,
// append the partialSpeechResults
setNote({ ...note, text: note.text + partialSpeechResults })
}
}
It's difficult to determine exactly where things might be going wrong without seeing a more complete code example. The // ...Other misc code
section of your example is kind of important, particularly where and how you're defining saveNote
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.