Reactjs 在钩子返回的函数中,状态保持不变

Reactjs 在钩子返回的函数中,状态保持不变,reactjs,react-hooks,Reactjs,React Hooks,我在玩React和Canvas。我想通过requestAnimationFrame设置正方形运动的动画。所以我制作了主要组件来处理画布元素和广场位置的更新: function App() { let canvas = useRef() let square = useSquare(WIDTH / 2, HEIGHT, 2) let update = () => { square.update() requestAnimationFra

我在玩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>
    </>
}
我想用一个钩子来封装正方形的状态和行为:

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 }
}
函数useSquare(init_x,init_y){
设[x,set_x]=useState(init_x)
让[y,set_y]=useState(init_y)
让[vx,setu vx]=useState(10)
让[vy,set_vy]=useState(-5)
让我们更新=()=>{
设置x(上一个x=>上一个x+vx)
设置y(上一个y=>上一个y+vy)
如果(x=宽度)设置_vx(上一个_vx=>-上一个_vx)
如果(y=高度)设置为(上一个=>-上一个)
}
返回{update,x,y}
}
对我来说似乎很合理。然而,它并没有像预期的那样工作。正方形正在移动,但它不会对与视口边界的碰撞作出响应

如果我将
console.log(x,y)
放入
update
函数中,我可以看到它以预期的速率启动,但状态没有改变

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


PS:我用示例制作了一个codesanbox,

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

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()
,这就是为什么您可以看到维度发生了更改


回答3:您正在通过调用
set**
来更新
Square
组件中的状态。问题是从
useSquare
返回的
update
函数是一个闭包,其中
x
y
的值永远不会改变,因此,
x=WIDTH
y=HEIGHT
的条件永远不会成立。下面是一个闭包示例:

设x=1;
功能更新(y){
返回函数(){
console.log(y);//始终为1
};
}
设置间隔(更新(x),1000);

x=2关于闭包是如何工作的,您是对的,但在本例中不是这样的,因为他使用的是通过prev state设置新状态
set_x(prev_x=>prev_x+vx)
,所以
prev_x
总是最新的。官方文档中的这个例子将解释更多:@TonyNguyen关于功能更新,你是对的。但是更新条件
(x=宽度)
(y=高度)
从来都不是真的,这会导致“正方形正在移动,但它不响应与视口边界的碰撞”的结果。
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;
}