在 React useEffect 中,你如何在长 运行 代码之间偷偷更新状态?
In React useEffect, how do you sneak state updates in between long running code?
我无法在 longRunningCode 执行之前显示我的加载状态。我试着让它异步无济于事。
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
const file = // synchronous code to generate a gzipped File object
return file;
}
// also tried
// const longRunningCode = async () => {
// ...
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
const result = longRunningCode()
// also tried `await` with async version
// ...
setLoadingStatus(undefined)
setMyAction(undefined)
}
}, [myAction])
//...
return (
<div>
<p>{loadingStatus}</p>
<button onClick={() => setMyAction({})}>Generate file</button>
</div>
)
useEffect 回调只允许 return undefined
或析构函数。在您的示例中,您传递了一个 return 是 Promise 的异步函数。它可能会或可能不会 运行,但您会看到 React 警告说 useEffect 回调是同步 运行。
相反,定义一个异步函数,然后调用它。
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
useEffect(() => {
const doAction = async () => {
if (myAction) {
setLoadingStatus('Do the thing...');
const result = await longRunningCode();
// ...
setLoadingStatus(undefined);
setMyAction(undefined);
}
};
doAction();
}, [myAction]);
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />
否则,你写的就可以了。
我在想一些事情:
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
return new Promise(resolve, reject)=>{
const file = // synchronous code to generate a File object
resolve(file);
}
}
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
longRunningCode().then(file=>{
// ...
setLoadingStatus(undefined)
setMyAction(undefined)
})
}
}, [myAction])
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />
**编辑:** 使用 setTimeout
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
const file = // synchronous code to generate a File object
return file
}
}
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
//a 0 second delay timer waiting for longrunningcode to finish
let timer = setTimeout(() =>{
longRunningCode()
setLoadingStatus(undefined)
setMyAction(undefined)
}, 0);
// clear Timmer on unmount
return () => {
clearTimeout(timer);
};
}
}, [myAction])
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />
我无法在 longRunningCode 执行之前显示我的加载状态。我试着让它异步无济于事。
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
const file = // synchronous code to generate a gzipped File object
return file;
}
// also tried
// const longRunningCode = async () => {
// ...
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
const result = longRunningCode()
// also tried `await` with async version
// ...
setLoadingStatus(undefined)
setMyAction(undefined)
}
}, [myAction])
//...
return (
<div>
<p>{loadingStatus}</p>
<button onClick={() => setMyAction({})}>Generate file</button>
</div>
)
useEffect 回调只允许 return undefined
或析构函数。在您的示例中,您传递了一个 return 是 Promise 的异步函数。它可能会或可能不会 运行,但您会看到 React 警告说 useEffect 回调是同步 运行。
相反,定义一个异步函数,然后调用它。
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
useEffect(() => {
const doAction = async () => {
if (myAction) {
setLoadingStatus('Do the thing...');
const result = await longRunningCode();
// ...
setLoadingStatus(undefined);
setMyAction(undefined);
}
};
doAction();
}, [myAction]);
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />
否则,你写的就可以了。
我在想一些事情:
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
return new Promise(resolve, reject)=>{
const file = // synchronous code to generate a File object
resolve(file);
}
}
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
longRunningCode().then(file=>{
// ...
setLoadingStatus(undefined)
setMyAction(undefined)
})
}
}, [myAction])
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />
**编辑:** 使用 setTimeout
const [loadingStatus, setLoadingStatus] = useState(undefined);
const [myAction, setMyAction] = useState(undefined);
const longRunningCode = () => {
const file = // synchronous code to generate a File object
return file
}
}
useEffect(() => {
if (myAction) {
setLoadingStatus('Do the thing...')
//a 0 second delay timer waiting for longrunningcode to finish
let timer = setTimeout(() =>{
longRunningCode()
setLoadingStatus(undefined)
setMyAction(undefined)
}, 0);
// clear Timmer on unmount
return () => {
clearTimeout(timer);
};
}
}, [myAction])
//...
return <p>{loadingStatus}</p><button onClick={() => setMyAction({})} />