Компонент React выполняет повторный рендеринг, однако эффект не запускается, хотя должен

Это мой компонент:

function Test() {
    const [data, setData]=useState<Array<string>>([]);
    console.info('Hello:', data);
    
    useEffect(()=>{
       console.info('data: ', data)
    }, [data])
    const test1 = () => {
        setData(data.concat('a'));
    }
    const test2 = () => {
        setData(data);
    }
    return (
        <>
           <button onClick = {test1}>Button one</button>
           <button onClick = {test2}>Button two</button>
       </>
    );
}

Все работает нормально при нажатии Button one. Компонент выполняет повторный рендеринг, и эффект запускается. Однако то, что происходит с Button two, я не могу объяснить:

  • Если щелкнуть Button two сразу после Button one, компонент перерендерится, но эффект не запустится. Это не имеет смысла, поскольку React использует сравнение Object.is для принятия решения о том, следует ли повторно отображать/запускать эффект. Как это сравнение дает разные результаты среди useState и useEffect? Сначала он решает перерендериться, что означает, что значение состояния data изменилось. Как это верно с setData(data)? Затем он решает не запускать эффект, что означает отсутствие изменений в зависимости data. Очевидно, что между двумя вышеуказанными решениями существует противоречие...
  • Если щелкнуть Button two во второй раз (после нажатия Button one), то ничего не произойдет, что абсолютно понятно.

Может ли кто-нибудь объяснить вышеуказанное поведение?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если новое состояние совпадает с текущим состоянием, как вы упомянули Object.is, React пропустит повторный рендеринг, но, как указано в документах React useState, React все еще может вызывать компонент.

Хотя в некоторых случаях React все еще может потребоваться вызвать ваш компонент, прежде чем пропустить дочерние элементы, это не должно повлиять на ваш код.

Это приводит к запуску console.info со значениями "Hello: ", data.

Итак, React на самом деле не перерисовывает компонент.

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

Эффект будет повторно запускаться после каждого повторного рендеринга компонента.

useEffect(() => {
  console.warn("Render");
});

Как видите, это не так.

const {useState, useEffect, Fragment} = React;

function Test() {
  const [data, setData] = useState([]);
  console.info("Hello:", data);

  useEffect(() => {
    console.warn("Render");
  });

  useEffect(() => {
    console.info("data: ", data);
  }, [data]);
  const test1 = () => {
    setData(data.concat("a"));
  };
  const test2 = () => {
    setData(data);
  };
  return (
    <Fragment>
      <button onClick = {test1}>Button one</button>
      <button onClick = {test2}>Button two</button>
    </Fragment>
  );
}

ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <Test />
);
<div id = "root"></div>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

что произошло, так это когда вы используете setData(data); повторное рендеринг компонента независимо от того, действительно ли данные изменились или нет, но когда дело доходит до useEffect способа сравнения значений, он берет старые значения и сравнивает их с новыми, если значения изменились. изменился, он выполнит то, что внутри него, иначе этого не произойдет, это поведение также произойдет, если данные являются объектом, он проверит каждое значение внутри него, чтобы решить, изменился ли объект или нет

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