Проблема useEffect EventListener REACT HOOKS

Я пытался сделать простую змеиную игру с помощью REACT. Все было хорошо, пока мне не понадобилось использовать useEffect для перемещения «змейки» при нажатии клавиши. Когда я пытаюсь поместить moveSnake() в useEffect, это дает мне некоторую ошибку. Когда я перемещаю его и называю внешним эффектом, получается бесконечный цикл. Я использую функциональные компоненты. App.js запутался, потому что я был в стрессе, потому что он не работает, и перепробовал все варианты. Надеюсь, у тебя все получится.

https://github.com/Pijano97/snake-game код здесь, спасибо. Также, если кто-то не может получить к нему доступ, вот код. Компонент змеи просто визуализирует змеиные точки с картой. Компонент еды просто создает случайную еду на карте.

import { useEffect, useState } from "react";
import "./App.css";
import Snake from "./Snake";
import Food from "./Food";

function App() {
    const getRandomFoodDot = () => {
        let min = 1;
        let max = 98;

        let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
        let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;

        return [x, y];
    };

    const [snakeDots, setSnakeDots] = useState([
        [0, 0],
        [2, 0],
    ]);
    const [foodDots, setFoodDots] = useState(getRandomFoodDot());
    const [direction, setDirection] = useState("");

    const moveSnake = () => {
        let dots = [...snakeDots];
        let head = dots[dots.length - 1];

        switch (direction) {
            case "UP":
                head = [head[0], head[1] - 2];
                break;
            case "DOWN":
                head = [head[0], head[1] + 2];
                break;
            case "LEFT":
                head = [head[0] - 2, head[1]];
                break;
            case "RIGHT":
                head = [head[0] + 2, head[1]];
                break;
            default:
                break;
        }
        // adding new head
        dots.push(head);
        // removing last dot
        dots.shift();
        setSnakeDots(dots);
    };

    moveSnake();

    useEffect(() => {
        const keyPressHandler = (e) => {
            switch (e.keyCode) {
                case 38:
                    setDirection("UP");
                    break;
                case 40:
                    setDirection("DOWN");
                    break;
                case 37:
                    setDirection("LEFT");
                    break;
                case 39:
                    setDirection("RIGHT");
                    break;
                default:
                    setDirection("");
                    break;
            }
        };
        document.addEventListener("keydown", keyPressHandler);
        return () => {
            document.removeEventListener("keydown", keyPressHandler);
        };
    }, []);

    console.info(direction);

    return (
        <div className = "app">
            <div className = "snake">
                <Snake snakeDots = {snakeDots} />
                <Food foodDots = {foodDots} />
            </div>
        </div>
    );
}

export default App;

Вы должны указать свою ошибку в вопросе

Sam 13.12.2020 03:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
257
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Причина, по которой вы получаете эту ошибку, в которой говорится, что React ограничивает количество повторных рендеров до проверенных бесконечных повторных рендеров, заключается в том, что вы вызываете moveSnake() внутри вашего компонента App(), а moveSnake() вызывает setSnakeDots(), который обновляет состояние змеиных точек. Когда состояние змеиных точек обновляется, ваш компонент приложения перерисовывается, что вызывает вызов moveSnake(), который вызывает setSnakeDots(), который обновляет состояние ваших змеиных точек, что приводит к повторному рендерингу вашего компонента приложения. Вы застряли в inifinate петле здесь.

Что-то, что могло бы исправить это (хотя я тестировал это, и игра не проходит дальше одного шага), могло бы установить вашу функцию moveSnake() в таймере, например

function timer(){
    setInterval(function() {
        moveSnake();
        timer()
    }, 1000);
}

timer()

На самом деле, у меня была эта функция, потому что она нужна мне для "движения каждые 300 мс". Хорошего дня! Это был мой первый пост

Mcmananman 13.12.2020 13:56
Ответ принят как подходящий

Я не уверен, правильно ли я понял, но, вероятно, вы можете попробовать это:

import { useEffect, useState } from "react";
import "./App.css";
import Snake from "./Snake";
import Food from "./Food";

const getDirection = e => {
    switch (e.keyCode) {
        case 38:
            return 'UP';
        case 40:
            return 'DOWN';
        case 37:
            return 'LEFT';
        case 39:
            return 'RIGHT';
        default:
            return '';
    }
}

function App() {
    const getRandomFoodDot = () => {
        let min = 1;
        let max = 98;

        let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
        let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;

        return [x, y];
    };

    const [snakeDots, setSnakeDots] = useState([
        [0, 0],
        [2, 0],
    ]);
    const [foodDots, setFoodDots] = useState(getRandomFoodDot());
    const [direction, setDirection] = useState({val: ''});

    useEffect(() => {
        let dots = [...snakeDots];
        let head = dots[dots.length - 1];

        switch (direction.val) {
            case "UP":
                head = [head[0], head[1] - 2];
                break;
            case "DOWN":
                head = [head[0], head[1] + 2];
                break;
            case "LEFT":
                head = [head[0] - 2, head[1]];
                break;
            case "RIGHT":
                head = [head[0] + 2, head[1]];
                break;
            default:
                break;
        }
        // adding new head
        dots.push(head);
        // removing last dot
        dots.shift();
        setSnakeDots(dots);
    }, [direction]);

    useEffect(() => {
        const keyPressHandler = (e) => {
            setDirection({ val: getDirection(e) });
        };
        document.addEventListener("keydown", keyPressHandler);
        return () => {
            document.removeEventListener("keydown", keyPressHandler);
        };
    }, []);

    console.info(direction);

    return (
        <div className = "app">
            <div className = "snake">
                <Snake snakeDots = {snakeDots} />
                <Food foodDots = {foodDots} />
            </div>
        </div>
    );
}

export default App;

Я только что создал getDirection функцию для получения направления от ключевого события и добавил еще одну useEffect с логикой из функции moveSnake Я не знал текущую функциональность вашего проекта, но приведенный выше код работает, и встряску можно двигать.

Это было здорово, просто добавили интервал в первый useEffect и условие для useEffect snakeDots.

Mcmananman 13.12.2020 14:16

Хорошо, мило. Всегда рад помочь ;)

Badyanchik 13.12.2020 22:17

Другие вопросы по теме