【问题标题】:How to await a setState call finishing when using useState hook that immediately needs that state to make an API call?使用立即需要该状态进行 API 调用的 useState 挂钩时,如何等待 setState 调用完成?
【发布时间】:2019-11-29 23:51:22
【问题描述】:

我有一个语音听写自定义挂钩,以及一个单独的自定义挂钩,该挂钩将听写结果附加到存储“Note”值的对象。

如果用户过早点击保存,我仍然需要在保存注释的 API 调用之前附加部分结果。

我的代码是这样的

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>
    )

}

问题是,正在调用 SaveNote 并且正在使用笔记的旧状态...状态设置未按时完成。

我似乎无法在此处使用 useEffect 挂钩来监视更改,因为我正在调用 API 来立即保存便笺,并且在保存时它正在访问便笺状态。

处理此问题的最佳方法是什么?谢谢。

【问题讨论】:

  • 目前useState hook中的setState不支持回调。但是您可以使用 useEffect 挂钩来执行“渲染后”回调并与当前状态值协调。
  • 是的,理解,但如上所述,我有一个独特的情况,我在之后立即使用该值将其保存在 API 调用中。
  • 也许这个答案可以帮助你stackoverflow.com/questions/53446020/…

标签: javascript reactjs react-hooks


【解决方案1】:

编辑

鉴于更新的代码,您应该能够非常类似于我在原始答案中概述的方式来处理它:

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

关键区别在于,如果您没有部分结果,您只能在新闻处理程序中调用 saveNote。这样你就不会得到不完整的保存。如果您setNote,它将放入您的useEffect 并以正确的值保存。

如果这是处理这些注释的常用模式,那么将这个逻辑移到您的 useNote 挂钩中可能是有意义的。


原答案

由于您使用useState 作为您的note 值,您应该能够使用useEffect 处理此问题。 useState 中的值是不可变的,因此它们可以很好地作为效果挂钩的输入。将您的电话移至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 })
  }
}

如果由于某种原因,您的 saveNote 函数在此组件内定义,我建议将其移到组件外并将 note 作为参数传递,这样您就可以确保 useEffect 仅在您运行时运行想要它。如果有一些令人信服的理由需要在组件内定义 saveNote,那么您应该使用 useCallback 定义 saveNote 并更改您的 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 })
  }
}

如果没有更完整的代码示例,很难准确地确定哪里出了问题。您示例的 // ...Other misc code 部分非常重要,尤其是您在何处以及如何定义 saveNote

【讨论】:

  • 感谢您的详细评论。刚刚用更多细节更新了我的代码。基本上,我将 useDictation 钩子的结果输入到我的组件中,如果在数组之前仍有部分结果返回,我想将其添加到注释中。我有多个钩子,其中包含 saveAPICalls,因此,如果我必须更新函数以接收笔记与在钩子内访问它,我必须更新很多钩子(我们有很多笔记类型)。
  • 这将是一个令人信服的理由来定义它内联:)
  • 呵呵 :) 不幸的是,我不能对笔记上的每个状态更新都进行 API 调用,这会杀死我的 AWS 账单 :)
【解决方案2】:

试试 useEffect 钩子:

编辑:因为它在第一次渲染时运行,所以你要确保在保存之前笔记对象不为空

useEffect(() => {

    if(Object.keys(note).length !== 0){

        saveNote();
    }

});

【讨论】:

  • 如果我理解正确,这是行不通的,因为这样它会在依赖项的每次更改或挂载时触发。
  • 正确——这将在每个渲染上运行,这对你的 api 来说不是很好。你需要一个依赖数组来防止它失控。
  • 可以在调用saveNote函数之前检查note对象是否为空。 @stephenlizcano
猜你喜欢
  • 2019-09-03
  • 2019-11-23
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-20
  • 2020-01-23
相关资源
最近更新 更多