Estoy escribiendo una aplicación web de codificación en vivo en React donde el usuario puede escribir un código javascript que mueve el ícono de un jugador a través de un laberinto. Quiero hacer todo esto en el frontend, por lo que estoy usando eval() para ejecutar el código del usuario en lugar de enviarlo a un backend para su evaluación. Estoy encontrando un comportamiento relacionado con eval() y la actualización de gráficos/estado de configuración que está causando un problema que no puedo solucionar. A continuación se muestra una versión simplificada del componente funcional encargado de mostrar los gráficos del laberinto. Acepta el code prop, que se actualiza cuando el usuario hace clic en el botón "ejecutar".
export default function MazeEngine(props) { const code = props.code; const [player, setPlayer] = useState({position: 0}); const canvasRef = useRef(null); useEffect(() => { runCode(); }, [code]) useEffect(() => { draw(canvasRef.current); }, [player]) const move = () => { setPlayer((pl) => {..pl, position: pl+1}); } const sleep = (ms) => { var start = new Date().getTime(), expire = start + ms; while (new Date().getTime() < expire) { } return; } } const runCode = () => { eval(code); } const draw = (ctx) => { // implementation not shown} return( <canvas ref={canvasRef} style={{width:350, height:350}}> ) } Lo que encontré es que si el usuario realiza varias llamadas a move() , todas las llamadas a setPlayer se procesan por lotes después de que se complete eval() . Entonces, por ejemplo, si el usuario escribe
move(); sleep(1000); move(); la pantalla se actualizará después de 1 segundo, con el jugador movido dos espacios hacia adelante. Verifiqué que el efecto en el player solo se llama una vez.
En cambio, me gustaría que el jugador avance un espacio, represente la nueva posición, espere un segundo y avance otro espacio. De esa manera, el usuario puede ver más fácilmente el comportamiento de su algoritmo.
Intenté un enfoque diferente en el que cambié al jugador para que fuera una referencia con useRef y llamé draw() directamente en el método de move en lugar de usar useEffect . Esto dio el mismo comportamiento problemático: el lienzo no se actualiza hasta que finaliza la eval .