Простая анимация Javascript в Gatsby

#javascript #reactjs #gatsby

#javascript #reactjs #gatsby

Вопрос:

Я новичок в изучении React и Gatsby и пытаюсь найти наилучший способ применить простую анимацию Javascript к элементу DOM внутри компонента. Я знаю, как обрабатывать события компонентов с помощью onClick и т. Д., Но, скажем, например, я хочу постоянно менять цвет a <span> в моем Header.js компоненте каждые 2 секунды.

 import React from 'react';

export default function Header() {
  return (
    <header>
      <p>This is a <span>test!</span></p>
    </header>
  )
}
  

Затем я бы хотел использовать некоторые JS, такие как:

 const spanEl = document.querySelector('header span');
let counter = 0;
const changeColor = () => {
  if (counter % 2 == 0) {
    spanEl.style.color = "red";
  } else {
    spanEl.style.color = "blue";
  }
  counter  ;
  if (counter == 10) counter = 0;
}
setInterval(changeColor, 2000);
  

Я обнаружил, что могу поместить это в тег script html.js перед закрывающим тегом body, но есть ли способ сохранить эту функциональность в компоненте? Нужно ли мне полностью переосмыслить свой подход при работе в этой среде?

Ответ №1:

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

Официальные документы React для хуков и эффектов очень хороши, я бы начал с этого.

 import React from 'react';

const noop = () => null;

// Encapsulate the interval behavior
const useInterval = (callback, delay) => {
    const savedCallback = useRef(noop);

    useEffect(() => {
        savedCallback.current = callback;
        savedCallback.current();
    }, [callback]);

    useEffect(() => {
        const id = setInterval(savedCallback.current, delay);

        return () => clearInterval(id);
    }, [delay]);
};

export default function Header() {
    const [color, setColor] = useState("blue");

    // setColor causes a re-render of the component
    const updateColor = setColor(color === "blue" ? "red" : "blue");

    useInterval(updateColor, 2000);

    // Use the jsx to change the color instead of reaching into the dom
  return (
    <header>
      <p>This is a <span style={{ color }}>test!</span></p>
    </header>
  )
}
  

Комментарии:

1. Расширяя ваш ответ, в реальном случае, когда анимация может находиться в более крупном и более сложном компоненте, возможно, стоит рассмотреть возможность создания отдельного компонента для анимированного текста, чтобы повторно отображать только тот, который меняется при изменении цвета.

2. Да, безусловно. Строго говоря, эти примеры не являются анимациями, а просто запланированными обновлениями компонентов. Если вам нужна настоящая анимация, вам придется использовать API анимации React.

Ответ №2:

[РЕДАКТИРОВАТЬ: я только что увидел ответ от @windowsill, который, я думаю, лучше моего; Я бы рекомендовал использовать это решение.]

В функциональном компоненте React вам нужно использовать useReference перехват для нацеливания на элемент (а не для его выбора document.querySelector() ) и useEffecet перехват для установки и удаления времени ожидания при монтировании / размонтировании компонента:

 import React, {
  useEffect,
  useRef,
  useCallback
} from 'react';

export function Header() {
  const animatedText = useRef(null);

  const runAnimation = useCallback(elem => {
    const currColour = elem.style.color;
    elem.style.color = (currColour === 'red' amp;amp; 'blue') || 'red';
  }, []);

  useEffect(() => {
    const animationInterval = setInterval(() => {
      runAnimation(animatedText.current);
    }, 2000);

    return () => {
      clearInterval(animationInterval);
    }
  }, [runAnimation]);

  return (
    <header>
      <p>This is a <span ref={animatedText}>test!</span></p>
    </header>
  );
}
  

useCallback Перехват используется в целях оптимизации и runAnimation предотвращает переопределение и инициализацию функции при каждом повторном рендеринге компонента.