Рендеринг пользовательского интерфейса в React JS

Недавно я узнал, что React визуализирует снимок пользовательского интерфейса до того, как будет вызвана функция set для изменения состояния и будет выполнен повторный рендеринг. Весь JSX работает с предыдущим состоянием. Например:

import { useState } from 'react';

export default function FeedbackForm() {
  const [name, setName] = useState('');

  function handleClick() {
    setName(prompt('What is your name?'));
    alert(`Hello, ${name}!`);
  }

  return (
    <button onClick = {handleClick}>
      Greet
    </button>
  );
}

После указания имени в поле подсказки предупреждение по-прежнему гласит: Hello, !. Это связано с тем, что предупреждение берет имя из первого рендеринга, которое имеет значение null.

Согласно приведенному выше описанию этот код:

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Hello');

  function handleSubmit(e) {
    e.preventDefault();
      alert(`You said ${message} to ${to}`);
  }

  return (
    <form onSubmit = {handleSubmit}>
      <label>
        To:{' '}
        <select
          value = {to}
          onChange = {e => setTo(e.target.value)}>
          <option value = "Alice">Alice</option>
          <option value = "Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder = "Message"
        value = {message}
        onChange = {e => setMessage(e.target.value)}
      />
      <button type = "submit">Send</button>
    </form>
  );
}

Таким образом, когда срабатывает событие onchange, изменяется состояние как переменной, так и сообщения, и когда форма отправляется, предупреждение должно отображать: You said Hello to Alice, скорее, оно показывает предупреждение, которое включает в себя новые to и новые message. Где я ошибаюсь?

Ваш вопрос не совсем ясен, имеет смысл, верно, что показано новое значение? Пожалуйста, включите минимально воспроизводимый пример и поясните реальную проблему.

0stone0 07.06.2024 17:45
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
61
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

В React.js setName() и другие изменения состояния выполняются не напрямую, а асинхронно. Это приводит к тому, что в вашем коде выводится старое имя. Простым решением было бы:

import { useState } from 'react';

export default function FeedbackForm() {
  const [name, setName] = useState('');

  function handleClick() {
    const newName = prompt('What is your name?');
    setName(newName);
    alert(`Hello, ${newName}!`);
  }

  return (
    <button onClick = {handleClick}>
      Greet
    </button>
  );
}

Извините, я не спросил об этом. Я хотел бы узнать, почему не возвращается второй код: вы сказали «Привет» Алисе.

wholesome 07.06.2024 17:24

Когда вызывается ваша функция React, DOM генерируется на основе ее текущего состояния. Если где-то в коде активируется перехватчик или создается событие, которое изменит DOM, произойдет повторный рендеринг. Таким образом, каждый раз, когда вы видите в коде «setX()», этот фрагмент кода будет «выполняться» не при текущем рендеринге, а через некоторое время после этого рендеринга и до следующего* рендеринга. Вот почему происходит бесконечный рендеринг, если вы вызываете хук при каждом рендеринге.

Обновлено: * «следующий» рендеринг означает более поздний рендеринг. Технически, если вы видите рендеринг, а затем вызывается перехват, потенциально между «этим» текущим рендерингом и «следующим» рендерингом может быть рендеринг.

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

Ваше понимание жизненного цикла рендеринга React и управления состоянием не совсем правильное. Событие onchange срабатывает всякий раз, когда входные данные изменяются. Когда это событие срабатывает, оно асинхронно ставит состояние в очередь на обновление через setTo и setMessage. Пока ничто не блокирует цикл событий, он будет обновлять состояние и повторно отображать (если есть что перерисовывать). Состояние на самом деле обновляется в следующем цикле цикла событий, но это будет выполняться в фоновом режиме, прежде чем вы успеете это заметить. Технически он не обновляется в вашей onchange рутине. В вашем втором примере нет ничего, что нужно было бы перерисовывать, поэтому вы не видите изменений в пользовательском интерфейсе. Однако состояние ЕСТЬ обновляется.

Причина, по которой вы видите Hello, ! в первом примере, заключается в том, что метод handleClick блокирует цикл событий до его завершения, что означает, что состояние name фактически не обновится до следующего цикла цикла событий.

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

Если вы хотите увидеть это с некоторой задержкой, вы можете просто изменить обработчик событий onchange на следующий:

{e => {console.info(to); console.info(message); setMessage(e.target.value)}}

В приведенном выше случае вы увидите предыдущее состояние переменных to и message до их обновления. Если вы внесете несколько изменений для запуска нескольких журналов консоли, вы увидите, что состояние действительно обновляется перед отправкой формы.

Вы «ставите в очередь» изменения состояния, помещая новое событие в очередь событий. JavaScript является однопоточным, поэтому цикл событий удаляет из очереди любое событие, которое находится следующим в очереди. Но если вы просто нажимаете на элементы в DOM, цикл событий наверняка выполняет события в фоновом режиме, потому что было бы крайне неэффективно блокировать, пока вы не нажмете определенную кнопку, чтобы обработать все изменения состояния.

Вот полный пример, который покажет вам, как состояние действительно обновляется при каждом событии onchange.

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Hello');

  function handleSubmit(e) {
    e.preventDefault();
      alert(`You said ${message} to ${to}`);
  }

  return (
    <form onSubmit = {handleSubmit}>
      <label>
        To:{' '}
        <select
          value = {to}
          onChange = {e => setTo(e.target.value)}>
          <option value = "Alice">Alice</option>
          <option value = "Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder = "Message"
        value = {message}
        onChange = {e => setMessage(e.target.value)}
      />
      <h1>To: {to}</h1>
      <p>Message: {message}</p>
      <button type = "submit">Send</button>
    </form>
  );

Добавление h1 и p не требуется, поскольку входные данные формы буквально используют текущие значения состояния переменных to и message, но размещение этих значений состояния за пределами входных данных может помочь вам увидеть, что происходит.

Я хотел бы узнать, почему не возвращается второй код: вы сказали «Привет» Алисе.

Потому что к моменту отправки формы изменения состояния уже обработаны. Это связано с тем, что событие onchange обновляет состояние асинхронно, и эти асинхронные события завершатся до того, как будет выполнено событие отправки формы.

До сих пор не могу получить первую часть. Можете ли вы уточнить? не знает о цикле событий.

wholesome 07.06.2024 17:40

Цикл событий — это суть вашего замешательства. Я добавил для вас немного больше контекста. Я надеюсь, что это помогает.

h0r53 07.06.2024 17:40

Можем ли мы так сказать: когда запускается onChange, компонент перерисовывается. Но этот повторный рендеринг не вызывает функцию handleSubmit, и, следовательно, никаких предупреждений не отображается. В этом случае, когда onChange запускается два раза, компонент визуализируется дважды, каждый раз меняя значение to и message в той последовательности, в которой onChange запускается первым. Теперь, когда срабатывает onSubmit, происходит третий повторный рендеринг с новым состоянием и сообщением, и handleSubmit вызывается с этими двумя новыми значениями. Это правильный путь?

wholesome 07.06.2024 17:49

Событие onChange не обновляет состояние напрямую. Он вызывает метод setState, который асинхронно обновляет состояние, и когда состояние компонента React обновляется, компонент выполняет повторную визуализацию (если есть что перерисовывать). Да, повторный рендеринг не приведет к выполнению предупреждения. Единственное, что вызывает alert, — это ваша handleSubmit рутина, которая происходит только тогда, когда вы нажимаете кнопку «Отправить». К тому времени, как вы нажмете эту кнопку, React обработает все инициированные асинхронные изменения состояния.

h0r53 07.06.2024 17:52

Да, я имею в виду, что onChange вызывает setState для изменения состояния и повторного рендеринга.

wholesome 07.06.2024 17:54

Но верно ли то, что компонент визуализируется трижды перед предупреждением?

wholesome 07.06.2024 17:54

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

h0r53 07.06.2024 17:55

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

h0r53 07.06.2024 17:58

Давайте продолжим обсуждение в чате.

wholesome 07.06.2024 18:06

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