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

Posted

技术标签:

【中文标题】使用立即需要该状态进行 API 调用的 useState 挂钩时,如何等待 setState 调用完成?【英文标题】:How to await a setState call finishing when using useState hook that immediately needs that state to make an API call? 【发布时间】: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 调用中。 也许这个答案可以帮助你***.com/questions/53446020/…。 【参考方案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

以上是关于使用立即需要该状态进行 API 调用的 useState 挂钩时,如何等待 setState 调用完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何立即更新 react useState?

如何使用 useState 设置对象数组的状态?

React useState() 使用指南

React Hooks:使用useState更新状态不会立即更新状态[重复]

act 和 useState 的单元测试错误,在 useEffect 中进行 API 调用

React useState 和 useEffect 混淆