Skip to content

Instantly share code, notes, and snippets.

@pointofpresence
Created May 23, 2020 11:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pointofpresence/de706f3b11a5f1457cd8d3a1637316b6 to your computer and use it in GitHub Desktop.
Save pointofpresence/de706f3b11a5f1457cd8d3a1637316b6 to your computer and use it in GitHub Desktop.

Побочные эффекты и хук useEffect

Хук useEffect позволяет выполнять из функционального компонента действия, которые вызывают побочные эффекты, например, получение данных с сервера, установка слушателей событий или взаимодействие с DOM-деревом.

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

// выбираем цвет из массива
// и устанавливаем его в качестве фона страницы
function App() {
  const [colorIndex, setColorIndex] = React.useState(0);
  const colors = ["blue", "green", "red", "orange"];

  // работа с DOM API из компонента - это побочный (сторонний) эффект
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
  });
  // теперь при любом изменении состояния запустится перерендер
  // и вызовется эффект-функция

  function handleChangeIndex() {
    const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
    setColorIndex(next);
  }

  return <button onClick={handleChangeIndex}>Change background color</button>;
}

Чтобы избежать вызова эффект-функции после каждого рендера, можно передать в хук второй аргумент – пустой массив.

function App() {
  ...
  // теперь, сколько бы мы ни кликали, цвет не поменяется
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
  }, []);
  // эффект-функция отработала только при первом монтировали

  return (
    <button onClick={handleChangeIndex}>
      Change background color
    </button>
  );
}

Что это за массив? Просто коллекция аргументов, при изменении которых должна отработать наша эффект-функция. Если массив пуст, то она сработает лишь единожды. Если мы положим туда, к примеру, colorIndex, то при каждом его изменении будет меняться фон страницы. Но если перерендер вызван чем-то другим, то коллбэк хука не будет вызван.

function App() {
  const [colorIndex, setColorIndex] = React.useState(0);
  const colors = ["blue", "green", "red", "orange"];

  // говорим react, что вызывать эффект нужно только при изменении colorIndex
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
  }, [colorIndex]);

  function handleChangeIndex() {
    const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
    setColorIndex(next);
  }

  return <button onClick={handleChangeIndex}>Change background color</button>;
}

Хук useEffect также позволяет выполнить какие-то действия при изменении компонента. Здесь можно, например, отписаться от прослушки событий DOM, чтобы не тратить память.

Нужно просто вернуть из эффект-функции другую функцию, и React вызовет ее при перерендере или удалении компонента.

function MouseTracker() {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  React.useEffect(() => {
    // устанавливаем слушатель
    window.addEventListener("mousemove", event => {
      const { pageX, pageY } = event;
      setMousePosition({ x: pageX, y: pageY });
    });

    // удаляем слушатель при изменении компонента
    return () => {
      window.removeEventListener("mousemove", event => {
        const { pageX, pageY } = event;
        setMousePosition({ x: pageX, y: pageY });
      });
    };
  }, []);

  return (
    <div>
      <h1>The current mouse position is:</h1>
      <p>
        X: {mousePosition.x}, Y: {mousePosition.y}
      </p>
    </div>
  );
}

Почение данных в useEffect

Сам коллбэк не может быть асинхронным (async), поэтому различные асинхронные операции нужно обрабатывать прямо внутри него или вынести в отдельную функцию:

const endpoint = "https://api.github.com/users/codeartistryio";

// пример с промисами
function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch(endpoint)
      .then(response => response.json())
      .then(data => setUser(data));
  }, []);
}

// пример с async/await
function App() {
  const [user, setUser] = React.useState(null);
  React.useEffect(() => {
    getUser();
  }, []);

  // используем отдельную асинхронную функцию
  async function getUser() {
    const response = await fetch("https://api.github.com/codeartistryio");
    const data = await response.json();
    setUser(data);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment