Компонент 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), то ничего не произойдет, что абсолютно понятно.

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

Создание микрофронтендов с Angular и федерацией модулей в монорепо: Пошаговое руководство
Создание микрофронтендов с Angular и федерацией модулей в монорепо: Пошаговое руководство
Микрофронтенды стали популярным архитектурным паттерном для веб-разработки. С появлением федерации модулей в Webpack 5 реализация микрофронтендов...
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
Революционная веб-разработка ServiceNow
Революционная веб-разработка ServiceNow
В быстро развивающемся мире веб-разработки ServiceNow для достижения успеха крайне важно оставаться на вершине последних тенденций и технологий. По...
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Заголовок веб-страницы играет наиболее важную роль в SEO, он помогает поисковой системе понять, о чем ваш сайт.
Руководство для начинающих по веб-разработке на React.js
Руководство для начинающих по веб-разработке на React.js
Веб-разработка - это захватывающая и постоянно меняющаяся область, которая постоянно развивается благодаря новым технологиям и тенденциям. Одним из...
Разница между Angular и React
Разница между Angular и React
React и AngularJS - это два самых популярных фреймворка для веб-разработки. Оба фреймворка имеют свои уникальные особенности и преимущества, которые...
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 способа сравнения значений, он берет старые значения и сравнивает их с новыми, если значения изменились. изменился, он выполнит то, что внутри него, иначе этого не произойдет, это поведение также произойдет, если данные являются объектом, он проверит каждое значение внутри него, чтобы решить, изменился ли объект или нет

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