React : 运行 异步函数在组件卸载之后但在另一个组件安装之前
React : Run async function after component unmounted but before another component mounts
希望有人可以帮助引导我朝着正确的方向前进,或者向我指出任何可能有帮助的 docs/learnings。
我有两个 React 组件:
<Widget/>
<Modal/>
当 <Widget/>
安装时,<Modal/>
被隐藏(反之亦然)使用条件渲染和我的 ShowModal
状态值中的相同布尔值(这个布尔值通过按钮切换).我为这个例子制作了一个简化的 CodeSandbox here
我卡住的地方是我有一个异步函数 takeScreenShot()
需要 运行 在 <Widget/>
卸载后 完全 和 before <Modal/>
安装。当调用 takeScreenShot()
时,<Widget/>
(刚刚卸载)和 <Modal/>
(即将安装)都不应该在 DOM 中可见。我需要确保是这种情况,因为我正在截取底层页面的屏幕截图,我不想在其中包含任何一个组件。在我的 CodeSandbox 示例中,我的屏幕截图函数的输出将呈现灰色背景,而不显示蓝色或红色框
我试过的
我试过 运行 在 <Widget/>
组件的 useEffect
挂钩中使用清理函数,就像这样。
useEffect(() => {
return () => takeScreenShot();
}, []);
但是它不起作用,因为清理功能类似于 componentWillUnmount()
运行s 在组件即将卸载时,而不是 完全 从 DOM 卸载。这导致我的屏幕截图捕获图像中的卸载组件。有没有人有想法为我指出正确的方向?
您可以在 useLayoutEffect
挂钩中尝试 运行 它,例如 ...
useLayoutEffect(() => {
if (!showModal) takeScreenshot();
}, [showModal]);
看...
https://reactjs.org/docs/hooks-reference.html#uselayouteffect
根据文档...
The signature is identical to useEffect, but it fires synchronously
after all DOM mutations. Use this to read layout from the DOM and
synchronously re-render. Updates scheduled inside useLayoutEffect will
be flushed synchronously, before the browser has a chance to paint.
您可能需要保持模式是否已加载的单独状态,以确保效果挂钩不会在组件的初始加载时执行。
您可以使用另一个状态布尔值来指示是否正在截取屏幕截图,如果是,则不渲染这两个元素中的任何一个。
所以像这样:
() => this.setState({screenshot: true}, () => this.takeScreenshot())
然后在您的 takeScreenshot 函数中,在您完成其执行后将其设置回 false
takeScreenshot = () => {
// ...
// ...
this.setState({screenshot: false})
}
现在正在渲染
{!screenshot && (showModal ? <Modal /> : <Widget />)}
以下是您可以(并且应该)执行的方法。
async function takeScreenshot() {
console.log("Taking screenshot...");
return new Promise(resolve => {
window.setTimeout(() => {
resolve();
console.log("Screenshot captured!");
}, 3000);
});
}
function Widget({ onUnmount }) {
React.useEffect(() => {
return () => onUnmount();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return <p>Widget</p>;
}
function Modal() {
return <p>Modal</p>;
}
function App() {
const [open, setOpen] = React.useState('widget');
const handleUnmount = async () => {
await takeScreenshot();
setOpen('modal');
}
return (
<React.Fragment>
{open === 'widget' && <Widget onUnmount={handleUnmount} />}
{open === 'modal' && <Modal />}
<button
onClick={() => {
if (open !== "widget") return;
setOpen("");
}}
>
Show modal
</button>
</React.Fragment>
)
}
您可以运行演示here。
将代码从您的应用移至模态框,以便在模态框首次加载时调用它。我删除了所有对 useEffect
或 useLayoutEffect
的引用,尽管 useLayoutEffect
在模态中可能仍然需要。
希望有人可以帮助引导我朝着正确的方向前进,或者向我指出任何可能有帮助的 docs/learnings。
我有两个 React 组件:
<Widget/>
<Modal/>
当 <Widget/>
安装时,<Modal/>
被隐藏(反之亦然)使用条件渲染和我的 ShowModal
状态值中的相同布尔值(这个布尔值通过按钮切换).我为这个例子制作了一个简化的 CodeSandbox here
我卡住的地方是我有一个异步函数 takeScreenShot()
需要 运行 在 <Widget/>
卸载后 完全 和 before <Modal/>
安装。当调用 takeScreenShot()
时,<Widget/>
(刚刚卸载)和 <Modal/>
(即将安装)都不应该在 DOM 中可见。我需要确保是这种情况,因为我正在截取底层页面的屏幕截图,我不想在其中包含任何一个组件。在我的 CodeSandbox 示例中,我的屏幕截图函数的输出将呈现灰色背景,而不显示蓝色或红色框
我试过的
我试过 运行 在 <Widget/>
组件的 useEffect
挂钩中使用清理函数,就像这样。
useEffect(() => {
return () => takeScreenShot();
}, []);
但是它不起作用,因为清理功能类似于 componentWillUnmount()
运行s 在组件即将卸载时,而不是 完全 从 DOM 卸载。这导致我的屏幕截图捕获图像中的卸载组件。有没有人有想法为我指出正确的方向?
您可以在 useLayoutEffect
挂钩中尝试 运行 它,例如 ...
useLayoutEffect(() => {
if (!showModal) takeScreenshot();
}, [showModal]);
看... https://reactjs.org/docs/hooks-reference.html#uselayouteffect
根据文档...
The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
您可能需要保持模式是否已加载的单独状态,以确保效果挂钩不会在组件的初始加载时执行。
您可以使用另一个状态布尔值来指示是否正在截取屏幕截图,如果是,则不渲染这两个元素中的任何一个。
所以像这样:
() => this.setState({screenshot: true}, () => this.takeScreenshot())
然后在您的 takeScreenshot 函数中,在您完成其执行后将其设置回 false
takeScreenshot = () => {
// ...
// ...
this.setState({screenshot: false})
}
现在正在渲染
{!screenshot && (showModal ? <Modal /> : <Widget />)}
以下是您可以(并且应该)执行的方法。
async function takeScreenshot() {
console.log("Taking screenshot...");
return new Promise(resolve => {
window.setTimeout(() => {
resolve();
console.log("Screenshot captured!");
}, 3000);
});
}
function Widget({ onUnmount }) {
React.useEffect(() => {
return () => onUnmount();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return <p>Widget</p>;
}
function Modal() {
return <p>Modal</p>;
}
function App() {
const [open, setOpen] = React.useState('widget');
const handleUnmount = async () => {
await takeScreenshot();
setOpen('modal');
}
return (
<React.Fragment>
{open === 'widget' && <Widget onUnmount={handleUnmount} />}
{open === 'modal' && <Modal />}
<button
onClick={() => {
if (open !== "widget") return;
setOpen("");
}}
>
Show modal
</button>
</React.Fragment>
)
}
您可以运行演示here。
将代码从您的应用移至模态框,以便在模态框首次加载时调用它。我删除了所有对 useEffect
或 useLayoutEffect
的引用,尽管 useLayoutEffect
在模态中可能仍然需要。