Переменная useState не обновляется в chrome отладчике, но обновляется в ReactDevTools - PullRequest
0 голосов
/ 27 апреля 2020

Я пытаюсь запустить массив с событием wheel. У меня есть ловушка useState, которая инициализируется со значением 0, когда компонент монтируется, и когда я провожу пальцем по трекпаду, если я провожу пальцем, уменьшение числа состояний 1 и наоборот. Это значение персонажа представляет текущую позицию индекса.

Все хорошо, событие и состояние работают так, как я ожидал. Проблема в том, что когда я пытаюсь выполнить некоторые условия, чтобы контролировать, что положение массива не больше или меньше размера этого массива в этом фрагменте кода:

if (Math.sign(e.deltaY) === -1) {
      console.log('swipe DOWN');
      if (persona > 0) {
        setPersona(prevPers => prevPers - 1);
      }
    } else {
      console.log('swipe UP');
      if (persona <= team.length) {
        setPersona(prevPers => prevPers + 1);
      }
    }

Когда я наблюдаю за персоной значение в Chrome Отладчик, значение равно 0 всегда, и из-за этого условия:

if (persona > 0) {
 setPersona(prevPers => prevPers - 1);
}

значение постоянно увеличивается, потому что значение персонажа всегда равно 0. Однако, когда я наблюдаю значение персоны с ReactDevTools значение меняется, если я касаюсь колеса.

Это мой код:

export const Equipo = () => {
  const container = useRef(null);
  const [persona, setPersona] = useState(0);

  const handleWheel = e => {
    e.stopPropagation();
    setEvent(false);

    if (Math.sign(e.deltaY) === -1) {
      console.log('swipe DOWN');
      if (persona > 0) {
        setPersona(prevPers => prevPers - 1);
      }
    } else {
      console.log('swipe UP');
      if (persona <= team.length) {
        setPersona(prevPers => prevPers + 1);
      }
    }

    setTimeout(() => {
      console.log('available to scroll');
      setEvent(true);
    }, 3000);
  };

  const setEvent = status => {
    status === true
      ? container.current.addEventListener('wheel', handleWheel, false)
      : container.current.removeEventListener('wheel', handleWheel, false);
  };

  useEffect(() => {
    setEvent(true);
  }, []);

  return (
    <div
      ref={container}
      className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
    >
      <div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
        {team[persona]}
      </div>
    </div>
  );
};

export default Equipo;

Я не знаю, почему значение в Chrome Отладчик отличается от того же значения в ReactDevTools. Это значение при отладке: Персональное значение в Chrome Отладчик Как мы видим, консольный журнал персоны равен 2, но при наведении курсора все еще выводит 0.

И это значение персонажа в ReactDevTools в той же точке: То же значение в ReactDevTools

Я не знаю, упускаю ли я что-то очень очевидное, но я не понимаю, почему в этом условное значение персоны по-прежнему составляет 0

1 Ответ

0 голосов
/ 27 апреля 2020

Итак, причина, по которой значение персонажа в вашей функции setEvent всегда равно 0, заключается в том, что вокруг него есть замыкание, которое создается при первом монтировании компонента и запуске ловушки useEffect.

Вот как я бы решил вашу проблему так же, как вы в настоящее время делаете:

Я переместил функции handleWheel и setEvent в функцию useEffect, чтобы указать, что их значения не меняются в течение срока действия эффекта. Я также избавился от ссылок на текущие значения persona, сделав так, чтобы вызовы setPersona не go выходили за их пределы.

export const Equipo = () => {
  const container = useRef(null);
  const [persona, setPersona] = useState(0);

  useEffect(() => {
    const handleWheel = (e) => {
      e.stopPropagation();
      setEvent(false);

      if (Math.sign(e.deltaY) === -1) {
        console.log('swipe DOWN');
        setPersona((prevPers) => {
          Math.max(0, prevPers - 1);
        });
      } else {
        console.log('swipe UP');
        setPersona((prevPers) => Math.min(team.length, prevPers + 1));
      }

      setTimeout(() => {
        console.log('available to scroll');
        setEvent(true);
      }, 3000);
    };

    const setEvent = (status) => {
      status === true
        ? container.current.addEventListener('wheel', handleWheel, false)
        : container.current.removeEventListener('wheel', handleWheel, false);
    };
    setEvent(true);
    return () => {
      setEvent(false);
    };
  }, []);

  return (
    <div
      ref={container}
      className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
    >
      <div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
        {team[persona]}
      </div>
    </div>
  );
};

export default Equipo;

Вот еще один способ сделать это используя функцию газа вместо тайм-аутов. Таким образом, я считаю, что это чище, так как вам не нужно беспокоиться о реферах и тайм-аутах. Это также более нормальный способ убедиться, что функция выполняется только каждый период времени t.

Вместо того, чтобы самостоятельно связывать прослушиватель событий, проще просто позволить реакции позаботиться об этом.

Сам код регулирования от https://blog.bitsrc.io/understanding-throttling-and-debouncing-973131c1ba07. Хотя вы можете так же легко использовать функцию газа loda sh.

export const Equipo = () => {
  const [persona, setPersona] = useState(0);
  const handleWheel = useCallback(
    throttle((e) => {
      e.stopPropagation();
      if (Math.sign(e.deltaY) === -1) {
        console.log('swipe DOWN');
        setPersona((prevPers) => {
          Math.max(0, prevPers - 1);
        });
      } else {
        console.log('swipe UP');
        setPersona((prevPers) => Math.min(team.length, prevPers + 1));
      }
    }, 3000),
    [team.length]
  );


  return (
    <div
      onWheel={handleWheel}
      className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
    >
      <div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
        {team[persona]}
      </div>
    </div>
  );
};

export default Equipo;

function throttle(f, t) {
  let lastCall;
  return function (args) {
    let previousCall = lastCall;
    lastCall = Date.now();
    if (
      previousCall === undefined || // function is being called for the first time
      lastCall - previousCall > t
    ) {
      // throttle time has elapsed
      f(args);
    }
  };
}

Объяснение того, почему персона всегда была 0:

In Javascript, когда функция создается, он создает то, что известно как замыкание, где он извлекает все переменные, которые существуют в его области видимости, и позволяет функции всегда иметь доступ к ним. MDN для замыканий

То, что происходило, состояло в том, что когда useEffect работал при монтировании, он получал текущую версию функции setEvent, и у него был бы доступ к текущей версии функции handleWheel. setEvent свяжет эту оригинальную версию handleWheel с div и никогда не сможет обновить привязанную версию handleWheel. Из-за замыканий исходная функция handleWheel имеет замыкание вокруг персонажа, когда персона была равна 0. И поскольку при каждом повторном рендеринге всегда есть новый экземпляр переменной персонажа, закрытая переменная никогда не обновлялась.

По сути, именно так выглядел ваш код в useEffect. Надеюсь, это поможет вам понять, почему у вас возникли проблемы.

useEffect(() => {
  const setEvent = (status) => {
    const handleWheel = (e) => {
      e.stopPropagation();
      setEvent(false);

      if (Math.sign(e.deltaY) === -1) {
        console.log('swipe DOWN');
        if (persona > 0) {
          setPersona((prevPers) => prevPers - 1);
        }
      } else {
        console.log('swipe UP');
        if (persona <= team.length) {
          setPersona((prevPers) => prevPers + 1);
        }
      }

      setTimeout(() => {
        console.log('available to scroll');
        setEvent(true);
      }, 3000);
    };
    status === true
      ? container.current.addEventListener('wheel', handleWheel, false)
      : container.current.removeEventListener('wheel', handleWheel, false);
  };
  setEvent(true);
}, []);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...