简体   繁体   English

在使用立即需要该状态的useState挂钩进行API调用时,如何等待setState调用结束?

[英]How to await a setState call finishing when using useState hook that immediately needs that state to make an API call?

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. 如果用户过早点击保存,则仍然需要在保存Note的API调用之前追加部分结果。

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. 问题是,正在调用SaveNote并且正在使用笔记的旧状态...状态的设置未按时完成。

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. 我似乎无法在这里使用useEffect钩子来监视更改,因为我正在调用API来立即保存注释并且在保存时它正在访问注释状态。

What is the best way to handle this? 处理这个问题的最佳方法是什么? Thank you. 谢谢。

Try the useEffect hook: 试试useEffect钩子:

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();
    }

});

EDIT 编辑

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. 关键的区别在于,如果您没有部分结果, saveNote在您的印刷机处理程序中调用saveNote That way you don't get incomplete saves. 这样你就不会得到不完整的保存。 If you setNote , it will drop into your useEffect and save with the right value. 如果你setNote ,它将进入你的useEffect并使用正确的值保存。

If this is a common pattern for handling these notes, it might make sense to move this logic into your useNote hook. 如果这是处理这些注释的常用模式,则将此逻辑移动到useNote钩子中可能是有意义的。


Original answer 原始答案

Since you're using useState for your note value, you should be able to handle this with useEffect . 由于您使用useState作为note值,因此您应该能够使用useEffect来处理它。 Values from useState are immutable, so they work great as inputs for effects hooks. useState中的值是不可变的,因此它们非常适合作为效果挂钩的输入。 Move your call to saveNote() outside of handleSavePress and into useEffect : 将您的调用移到saveNote()之外的handleSavePress saveNote()并进入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. 如果由于某种原因,你的saveNote功能是该组件中定义的,我会建议移动它的组件外,并传递给它note作为参数,所以你可以肯定useEffect ,当你希望它才会运行。 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: 如果有,为什么你需要定义一些令人信服的理由saveNote组件里面,那么你应该定义saveNoteuseCallback和改变你的useEffect功能键关机的变化是:

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 . // ...Other misc code示例中的// ...Other misc code部分非常重要,特别是在定义saveNote位置和方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM