Боже мой, почему мой JavaScript не может найти мой компонент React. ЧТО Я ДЕЛАЮ НЕ ТАК!!!!?!?

Это, вероятно, то, что в какой-то момент говорил каждый разработчик, начинающий с React. К счастью, создатели React дали нам возможность бороться с этим.

Любой код, изменяющий компонент после его монтирования, называется «побочным эффектом». Для этого нам, разработчикам, остается использовать хук useEffect.

В приведенном ниже примере мы пытаемся заменить textContent элемента абзаца новым текстом. Но при такой настройке будет выброшена ошибка и никаких изменений не будет. ПОЧЕМУ?

function Content(){

  document.getElementById("content").textContent = "New Text";

  return(
    <p id="content">Replace This Text</p>
  )
}

Я объясню вам, почему компоненты в React остаются объектами JavaScript до тех пор, пока они не будут смонтированы/отрендерены. В этом примере оператор для изменения textContent абзаца фактически выполняется ДОвозвращения компонента React, а тем более при монтировании.

Итак, что вы можете сделать, чтобы получить доступ к компоненту ПОСЛЕ того, как он будет отображен в DOM как HTML?

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

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

function Content(){

  React.useEffect(()=>{
    document.getElementById("content").textContent = "New Text";
  }); 

  return(
    <p id="content">Replace This Text</p>
  )
}

Угадайте, что? Код работает, и теперь textContent нашего абзаца будет изменен на "Новый текст".

А как насчет асинхронных данных????

Если у нас есть значение, которое мы получаем из API. Нам как бы нужно обновить часть нашего приложения, которая полагается на эти данные, как только мы их получим, верно? ПРАВИЛЬНО.

В приведенном ниже примере я изменил наш компонент Content, чтобы полагаться на некоторые данные из API. Используя useEffect, мы определяем и используем асинхронную функцию для извлечения наших данных и установки состояния нашего текста в текст ответа.

function Content(){

const [contentText, setContentText] =  React.useState("Replace This Text");

React.useEffect(()=>{
  getData();
  async function getData(){
    const fetchData = await fetch("https://urltoapi.com");
    const textData = await fetchData.text();
    setContentText(textData);
  }
}); 

  return(
    <p id="content">{contentText}</p>
  )
}

React не допускает, чтобы функция, передаваемая в useEffect, была асинхронной. Поэтому вы должны определить его ВНУТРИ переданной функции и выполнить его там.

Но Уильям, мой код запускается каждый раз, когда компонент обновляется, и это нарушает мой код!

Да, да, это так. Это по дизайну. По умолчанию useEffect запускается каждый раз, когда компоненту необходимо обновиться. Для тех из нас, кому нужно ограничить это, например, когда вы разрабатываете SPA (одностраничное приложение) и вам нужно вручную запускать useEffect в определенное время, к счастью, снова нас спасает React создатели.

useEffect принимает второй необязательный аргумент. Массив состояний/значений зависимостей. Всякий раз, когда изменяется одно из этих значений состояния, происходит повторный запуск хука useEffect . Будь то обновление компонента или любое другое время между ними.

Ниже я изменил наш компонент Content, чтобы он запускал useEffect каждый раз, когда нажимается кнопка «Trigger useEffect», которая изменяет состояние triggerCount, и это состояние является значением в массив зависимостей useEffect.

function Content(){
  
  const [triggerCount, setTriggerCount] = React.useState(1);

  function handleClick(){
    setTriggerCount((oldval) => ++oldval);
  }

  React.useEffect(()=>{
    console.log(`useEffect has been triggered ${triggerCount} time(s)!`);
  }, [triggerCount]);

  return(
    <button onClick={handleClick}>Trigger useEffect</button>
  )
}

Но я хочу, чтобы это срабатывало только ОДИН раз!!!

Что ж, если вы укажете useEffect пустой массив в качестве массива зависимостей, хук сработает только один раз, когда компонент будет смонтирован/отрендерен. Это связано с тем, что когда вы предоставляете массив зависимостей, useEffect ожидает изменения одного из этих значений перед повторным запуском. Но если вы не укажете никаких значений, они не могут измениться, поэтому он не будет запускаться повторно.

Итак, для тех, кто учится визуально, вот изменение первого примера, где useEffect срабатывает только один раз.

function Content(){

  React.useEffect(()=>{
    document.getElementById("content").textContent = "New Text";
  }, []); 

  return(
    <p id="content">Replace This Text</p>
  )
}

Так что это все для этой статьи. Я очень надеюсь, что вы узнали что-то новое и получили удовольствие от этого процесса. Я потратил немного времени на написание этого, так что если вам понравилось читать, дайте мне знать :D.

Для получения дополнительных советов, приемов и руководств следуйте за мной на YouTube или Instagram.