如何在memoizedTodos渲染到屏幕后执行handleOpen函数?
实现方案
要在memoizedTodos对应的大量内容(包括文本、图片)完全显示在屏幕后执行handleOpen,核心是要等DOM渲染完成+所有异步资源(如图片)加载完毕,以下是几种可行方案:
方案一:纯DOM元素(无异步资源)
如果memoizedTodos里只有文本、普通DOM元素,没有图片这类需要异步加载的资源,可以用useEffect结合requestAnimationFrame:
import React, { useMemo, useEffect } from "react"; import "./styles.css"; export default function App() { const handleOpen = () => { console.log("this is running"); }; const memoizedTodos = useMemo(() => { return [...Array(1000)].map((e, i) => <li key={i}>This is so many data</li>); }, []); useEffect(() => { // requestAnimationFrame会等待浏览器完成下一次重绘后执行 const rafId = requestAnimationFrame(() => { handleOpen(); }); return () => cancelAnimationFrame(rafId); }, [memoizedTodos]); // 依赖memoizedTodos,确保内容更新后重新触发 return ( <div className="App"> <h1>Hello CodeSandbox</h1> <>{memoizedTodos}</> </div> ); }
useEffect会在组件DOM更新完成后触发,而requestAnimationFrame会等待浏览器把更新后的DOM绘制到屏幕上,此时内容已经可见,适合纯同步渲染的场景。
方案二:包含图片等异步资源
如果memoizedTodos里有图片,必须等所有图片加载完成后内容才会完全显示,这时候需要监听所有图片的load事件:
import React, { useMemo, useEffect, useRef } from "react"; import "./styles.css"; export default function App() { const handleOpen = () => { console.log("this is running"); }; const todosContainerRef = useRef(null); const memoizedTodos = useMemo(() => { return [...Array(1000)].map((e, i) => ( <li key={i}> This is so many data <img src={`https://picsum.photos/200/200?random=${i}`} alt={`todo-${i}`} /> </li> )); }, []); useEffect(() => { const container = todosContainerRef.current; if (!container) return; const images = container.querySelectorAll("img"); if (images.length === 0) { // 没有图片,直接执行 requestAnimationFrame(handleOpen); return; } // 用Promise等待所有图片加载完成 const imageLoadPromises = Array.from(images).map(img => { // 如果图片已经加载完成,直接返回resolved Promise if (img.complete) return Promise.resolve(); return new Promise(resolve => { img.addEventListener("load", resolve, { once: true }); // 处理图片加载失败的情况,避免一直等待 img.addEventListener("error", resolve, { once: true }); }); }); (async () => { await Promise.all(imageLoadPromises); // 所有图片加载完成后,等待重绘 requestAnimationFrame(handleOpen); })(); }, [memoizedTodos]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <div ref={todosContainerRef}>{memoizedTodos}</div> </div> ); }
这里用Promise.all监听所有图片的加载状态,等全部加载(或失败)后,再通过requestAnimationFrame确保内容绘制到屏幕上,最后执行handleOpen。
关于async/await的使用
async/await适合用来处理异步资源加载的场景(比如方案二中的图片加载),把异步等待的逻辑变得更清晰,但它本身不能直接监听DOM渲染完成——必须结合useEffect和requestAnimationFrame来配合使用,才能确保内容完全可见后执行回调。
内容的提问来源于stack exchange,提问作者Happy1234
相关产品推荐
相关产品推荐

