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