React中如何在子组件中使用父组件的Ref?
React Canvas游戏组件拆分与Ref传递解决方案
核心问题梳理
当前代码有两个关键错误导致子组件无法正常工作:
<canvas>是HTML替换元素,内部的React子组件不会被浏览器渲染(仅作为不支持canvas时的降级内容),所以<Background/>和<Player/>根本没执行逻辑- 即便用了
forwardRef,也没正确传递父组件的canvas ref,且子组件实际需要的是绘图上下文context而非原始ref
修正后的代码实现
1. 父组件Canvas.jsx(重构结构,传递上下文与资源)
import { useRef, useEffect, useState } from 'react' import Background from './Background' import Player from './Player' function Canvas() { const myCanvasRef = useRef(null) const [canvasCtx, setCanvasCtx] = useState(null) const [allImagesLoaded, setAllImagesLoaded] = useState(false) // 预加载游戏资源 const backgroundImg = new Image() const playerImg = new Image() useEffect(() => { let loadedCount = 0 const checkLoadComplete = () => { loadedCount++ if (loadedCount === 2) setAllImagesLoaded(true) } // 绑定加载事件 backgroundImg.onload = checkLoadComplete playerImg.onload = checkLoadComplete // 设置资源路径 backgroundImg.src = 'src/assets/PelletTown.png' playerImg.src = 'src/assets/playerRight.png' // 初始化Canvas const canvas = myCanvasRef.current const ctx = canvas.getContext('2d') canvas.width = 1024 canvas.height = 576 setCanvasCtx(ctx) // 清空画布 ctx.fillStyle = 'black' ctx.fillRect(0, 0, canvas.width, canvas.height) // 清理函数 return () => { backgroundImg.onload = null playerImg.onload = null } }, []) return ( <> <canvas ref={myCanvasRef} /> {/* 确保上下文和资源就绪后再渲染子组件 */} {canvasCtx && allImagesLoaded && ( <> <Background ctx={canvasCtx} img={backgroundImg} /> <Player ctx={canvasCtx} img={playerImg} /> </> )} </> ) } export default Canvas
2. 子组件Player.jsx(接收上下文,执行绘制逻辑)
import { useEffect } from "react"; const Player = ({ ctx, img }) => { useEffect(() => { // 绘制玩家 ctx.drawImage(img, 0, 0) // 如果需要动态更新(比如移动动画),可以在这里添加帧循环 const animId = requestAnimationFrame(() => { // 后续更新逻辑写在这里 }) return () => cancelAnimationFrame(animId) }, [ctx, img]) // 子组件不需要渲染DOM,所有绘制都在父组件的canvas上完成 return null } export default Player
3. 子组件Background.jsx(同理实现)
import { useEffect } from "react"; const Background = ({ ctx, img }) => { useEffect(() => { ctx.drawImage(img, -700, -500) }, [ctx, img]) return null } export default Background
关键说明
- Canvas内部不能放React组件:
<canvas>的内部内容仅作为降级显示,浏览器不会解析其中的React组件,必须把组件放在canvas外部 - 直接传递上下文更高效:子组件需要的是绘图上下文
ctx,直接通过props传递比传递ref再手动获取ctx更符合React数据流,也避免了ref传递的复杂操作 - 统一管理资源加载:确保所有图片加载完成后再绘制,避免出现空白或绘制失败的情况
内容的提问来源于stack exchange,提问作者Frigyes Vass
相关产品推荐
相关产品推荐

