Reactjs 使用React.usemo的异步调用

Reactjs 使用React.usemo的异步调用,reactjs,async-await,react-usememo,Reactjs,Async Await,React Usememo,场景相对简单:我们有一个在远程服务器上进行的长时间运行的按需计算。我们想把结果记录下来。即使我们从远程资源异步获取数据,这也不是一个副作用,因为我们只希望将计算结果显示给用户,而我们绝对不希望在每次渲染时都这样做 问题:React.UseMoom似乎不直接支持Typescript的async/await,并将返回一个承诺: //returns a promise: let myMemoizedResult = React.useMemo(() => myLongAsyncFunction

场景相对简单:我们有一个在远程服务器上进行的长时间运行的按需计算。我们想把结果记录下来。即使我们从远程资源异步获取数据,这也不是一个副作用,因为我们只希望将计算结果显示给用户,而我们绝对不希望在每次渲染时都这样做

问题:React.UseMoom似乎不直接支持Typescript的async/await,并将返回一个承诺:

//returns a promise: 
let myMemoizedResult = React.useMemo(() => myLongAsyncFunction(args), [args])
//also returns a promise:
let myMemoizedResult = React.useMemo(() => (async () => await myLongAsyncFunction(args)), [args])
等待异步函数的结果并使用React.useMoom记录结果的正确方法是什么?我在普通JS中使用了常规承诺,但在这种情况下仍然难以实现

我尝试过其他方法,如memoize one,但问题似乎是,
this
上下文因React函数组件的工作方式而改变,这就是我尝试使用React.UseMoom的原因

也许我想把一个方形的钉子装进一个圆形的洞里——如果是这样的话,知道这一点也很好。现在,我可能只想推出我自己的记忆功能

编辑:我想部分原因是我在memoize one上犯了一个不同的愚蠢错误,但我仍然有兴趣知道答案wrt React.memo

这里有一个片段-其想法不是直接在render方法中使用记忆结果,而是以事件驱动的方式(即单击“计算”按钮)作为参考

export const MyComponent: React.FC = () => {
    let [arg, setArg] = React.useState('100');
    let [result, setResult] = React.useState('Not yet calculated');

    //My hang up at the moment is that myExpensiveResultObject is 
    //Promise<T> rather than T
    let myExpensiveResultObject = React.useMemo(
        async () => await SomeLongRunningApi(arg),
        [arg]
    );

    const getResult = () => {
        setResult(myExpensiveResultObject.interestingProperty);
    }

    return (
        <div>
            <p>Get your result:</p>
            <input value={arg} onChange={e => setArg(e.target.value)}></input>
            <button onClick={getResult}>Calculate</button>
            <p>{`Result is ${result}`}</p>
        </div>);
}
导出常量MyComponent:React.FC=()=>{ let[arg,setArg]=React.useState('100'); let[result,setResult]=React.useState('尚未计算'); //我现在的问题是我的ExpensiveResultObject是 //承诺而不是承诺 让myExpensiveResultObject=React.UseMoom( async()=>等待SomeLongRunningApi(arg), [arg] ); const getResult=()=>{ setResult(myExpensiveResultObject.interestingProperty); } 返回( 获得您的结果:

setArg(e.target.value)}> 算计 {`Result是${Result}`}

); }
编辑:由于调用的异步性质,我下面的原始答案似乎有一些意外的副作用。相反,我会尝试在服务器上记录实际的计算,或者使用一个自行编写的闭包来检查
arg
是否没有更改。否则,您仍然可以像我下面描述的那样使用
useffect

我认为问题在于
async
函数总是隐式返回一个承诺。由于是这种情况,您可以直接
等待
结果以解除承诺:

const getResult = async () => {
  const result = await myExpensiveResultObject;
  setResult(result.interestingProperty);
};
看一看


我确实认为更好的模式可能是利用
useffect
,该模式依赖于某个状态对象,在这种情况下只能通过单击按钮进行设置,但似乎
usemo
也应该可以工作。

您真正想要的是在异步调用结束后重新呈现组件。仅仅写回忆录并不能帮助你实现这一目标。相反,您应该使用React的状态-它将保留异步调用返回的值,并允许您触发重新呈现

此外,触发异步调用是一种副作用,因此它不应该在渲染阶段执行-既不在组件函数的主体内部,也不在
usemo(…)
内部,这同样发生在渲染阶段。相反,所有副作用都应该在
useffect
内部触发

以下是完整的解决方案:

const [result, setResult] = useState();
useEffect(() => {
  let active = true
  load()
  return () => { active = false }

  async function load() {
    setResult(undefined) // this is optional
    const res = await someLongRunningApi(arg1, arg2)
    if (!active) { return }
    setResult(res)
  }
}, [arg1, arg2])
这里我们调用
useffect
中的异步函数。请注意,您不能在
useffect
async中进行整个回调-这就是为什么我们在内部声明一个异步函数
load
,并在不等待的情况下调用它

arg
s中的一个发生更改时,效果将重新运行-这是大多数情况下所需的。因此,如果在渲染时重新计算,请确保记住
arg
s。执行
setResult(未定义)
是可选的-您可能希望在屏幕上保留上一个结果,直到获得下一个结果。或者您可以执行类似于
setLoading(true)
的操作,以便用户知道发生了什么

使用
active
标志非常重要。没有它,您将面临等待发生的竞争条件:第二个异步函数调用可能在第一个异步函数调用完成之前完成:

  • 启动第一个呼叫
  • 开始第二次通话
  • 第二次调用完成后,
    setResult()
  • 第一次调用完成后,
    setResult()
    再次发生,覆盖 正确的结果与陈旧的结果
  • 组件最终会处于不一致的状态。我们通过使用
    useffect
    的清理功能来重置
    active
    标志来避免这种情况:

  • 设置
    active#1=true
    ,开始第一次呼叫
  • 参数更改,调用清除函数,设置
    active#1=false
  • 设置
    active#2=true
    ,开始第二次呼叫
  • 第二次调用完成后,
    setResult()
  • 第一次调用完成后,
    setResult()
    不会发生,因为
    active#1
    false

  • 我认为React特别提到了不应用于管理异步API调用等副作用。它们应该在
    useffect
    钩子中进行管理,钩子中设置了适当的依赖项,以确定它们是否应该重新运行。

    为什么不
    让myMemoizedResult=Wait React.useMoom(()=>myLongAsyncFunction(args),[args])
    @GazihanAlankus这会不会导致一个记忆承诺每次都回调用长时间运行的异步函数,即使参数没有改变?对不起,我不知道UseMoom,我只是认为你在async/Wait方面有问题