首页 > 解决方案 > 状态在钩子返回的函数中没有改变

问题描述

我在玩 React 和 Canvas。我想为一个正方形的运动设置动画requestAnimationFrame。所以我制作了主要组件来处理画布元素和更新正方形的位置:

function App() {
    let canvas = useRef()
    let square = useSquare(WIDTH / 2, HEIGHT, 2)
    let update = () => {
        square.update()
        requestAnimationFrame(update)
    }
    useEffect(update, [])
    return <>
        <canvas ref={canvas} width={WIDTH} height={HEIGHT} />
        <Renderer.Provider value={() => ref.canvas.getContext("2d")}>
            <Square {...square} />
        </Renderer.Provider>
    </>
}

Square是一个组件,但它不返回 JSX。它在以下位置运行绘图操作useEffect

function Square({ x, y }) {
    let getContext = useContext(Renderer)
    useEffect(() => {
        let ctx = getContext()
        ctx.fillStyle = "#000"
        ctx.fillRect(x, y, 10, 10)
    }, [x, y])
    return null;
}

我想用一个钩子来封装正方形的状态和行为:

function useSquare(init_x, init_y) {
    let [x, set_x] = useState(init_x)
    let [y, set_y] = useState(init_y)
    let [vx, set_vx] = useState(10)
    let [vy, set_vy] = useState(-5)
    let update = () => {
        set_x(prev_x => prev_x + vx)
        set_y(prev_y => prev_y + vy)
        if (x <= 0 || x >= WIDTH) set_vx(prev_vx => -prev_vx)
        else if (y <= 0 || y >= HEIGHT) set_vy(prev_vy => -prev_vy)
    }
    return { update, x, y }
}

对我来说似乎很合理。但是,它没有按预期工作。Square 正在移动,但它不会响应与视口边界的碰撞。

如果我console.log(x, y)输入任何一个update函数,我可以看到它以预期的速率触发,但状态没有改变。

我不明白如果状态没有改变,为什么组件能够正确呈现。坦率地说,我不知道我做错了什么。也许,有人帮我澄清一下。提前致谢。

PS:我用示例https://codesandbox.io/s/xenodochial-jepsen-nfxv6制作了一个codesanbox

标签: reactjsreact-hooks

解决方案


Answer1:问题是每次requestAnimationFrame运行时,square.update()您实际上都是在创建新框。您需要在绘制新框架之前清除框架

import { useContext, useEffect } from "react";
import { Renderer } from "./ctx";

export function Square({ x, y }) {
  let getContext = useContext(Renderer);
  useEffect(() => {
    let ctx = getContext();
    // clear before drawing
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
    ctx.fillStyle = "#000";
    ctx.fillRect(x, y, 10, 10);
  }, [x, y]);
  return null;
}

检查代码框以进行可视化

这是我在网上找到的一个非常好的系列。一探究竟

答案2: requestAnimationFrame创建一个循环来调用square.update(),这就是为什么你可以看到尺寸改变了

Answer3:您正在Square通过调用更新组件内部的状态set**


推荐阅读