React 18 новый способ обновления состояния или нет?

Я столкнулся со своеобразной проблемой с обновлениями состояния в React, это не ошибка, мой код работал нормально, но я чувствую себя немного странно. Изначально у меня есть состояние продукта, содержащее все продукты, полученные из API:

const [products, setProducts] = useState<Product[]>([]);

Я создал функцию для изменения информации о продукте. Несмотря на то, что я не обновлял состояние предыдущих продуктов, описание все равно меняется на новое:

const handleChangeEventProduct = async ({ productId }: Product) => {
  const exitsProduct = products.find(({ id }) => id === productId);
  exitsProduct.description = "example desc";
  const response = await fetch(
    `${process.env.VITE_APP_API_URL}/products/add`,
    {
      method: 'POST',
      body: JSON.stringify(exitsProduct),
    }
  );
  const responseObj = await response.json();
  // Update the products state, I don't know why the products state is update the description to `example desc`.
  if (responseObj?.success) {
    setProducts(products);
  }
} 

С тех пор как я начал работать с React, я считаю этот метод правильным для обновления состояния.

const updatedProducts = products.map((product) => {
  if (product.id === productId) {
    return {
     ...product,
     description: product.description,
    }
  }
  return product;
});
setProducts(updatedProducts);

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

Поведение ключевого слова "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
0
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я думаю, вы знаете, что показанный handleChangeEventProduct нарушает фундаментальное правило состояния React: не изменяйте члены состояния напрямую. Я думаю, вы спрашиваете, почему вы видите обновленное описание, если функция делает это неправильно.

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

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

const { useState, useRef } = React;

const Example = () => {
    const [counter, setCounter] = useState(0);
    const [object, setObject] = useState({ property: 0 });

    const update = (updateCounter) => {
        ++object.property; // <=== WRONG, don't do this
        setObject(object); // <=== Does nothing
        if (updateCounter) {
            setCounter((c) => c + 1);
        }
    };

    return (
        <div>
            <input
                type = "button"
                value = "Update Just Property"
                onClick = {() => update(false)}
            />
            <input
                type = "button"
                value = "Update Property and Counter"
                onClick = {() => update(true)}
            />
            <div>counter: {counter}</div>
            <div>property: {object.property}</div>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id = "root"></div>

<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

Примечание: вызов setProducts(products);handleChangeEventProduct не обязательно что-либо сделает (и, по моему опыту, не делает), потому что products (массив) не изменился (по крайней мере, в показанном коде), даже несмотря на то, что свойство один из объектов массива изменился.

Спасибо, но если я удалю setProducts(products); часть, описание не изменится!

Viet Nguyen 26.04.2024 11:46

@VietNguyen - Ну, нет никаких гарантий, что React не выполнит рендеринг, просто нет гарантий, что он это сделает (и я не могу это повторить - можете ли вы предоставить минимальный воспроизводимый пример?), React может решить для повторной визуализации вашего компонента в любое время. Но нет никаких сомнений в том, что handleChangeEventProduct обновляет объект неправильно.

T.J. Crowder 26.04.2024 11:50

Это так странно, вот мой экран кода и красная линия, если я ее удалю, состояние никогда не обновится. Пожалуйста, посмотрите здесь ibb.co/g3GkfTk. Я уже пытался воспроизвести проблему здесь, но мне это не удалось.

Viet Nguyen 26.04.2024 12:23

@VietNguyen - Опять же, нет никаких гарантий, что React сделает с неправильным обновлением. Фундаментальным моментом здесь является то, что если вы хотите обновить продукт в этом массиве, ваш код в updatedProducts — правильный способ сделать это. Если вы напрямую изменяете объект в состоянии, вы понятия не имеете, когда и будет ли это изменение отображено.

T.J. Crowder 26.04.2024 13:01

Я нашел ответ на этот вопрос, потому что я использую режим реагирования, и каким-то образом этот модальный режим вызывает изменение. Вы можете увидеть это здесь codeandbox.io/p/devbox/react-state-4nvrfr. На самом деле exitsProduct.description = "example desc"; никогда не обновляет состояние!

Viet Nguyen 26.04.2024 15:40

@VietNguyen - Это облегчение, мне действительно было интересно. :-) Хорошая находка!

T.J. Crowder 26.04.2024 17:34

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

Проблема с компонентом рендеринга, созданная с использованием vite-plugin-svgr в шуточном тесте с использованием метода рендеринга библиотеки тестирования/реагирования
Асинхронные проблемы с useState в React Hooks
Ошибка: только простые объекты и несколько встроенных функций могут быть переданы клиентским компонентам из серверных компонентов. Классы или нулевые прототипы не поддерживаются
Как дождаться завершения вызовов API, прежде чем создавать еще один, когда они находятся в своих собственных функциях?
Eslint – невозможно разрешить путь к модулю @web3modal/ethers/react, import/no-unresolved
Сбой подключения NodeJS к CosmosDB при создании клиента из строки подключения
Как применить разные стили для многоразовой кнопки реагирования с помощью Mui
Как запустить повторный рендеринг компонента реагирования после изменения параметра запроса (без реагирования-маршрутизатора)
Медленный запуск vite с булочкой
Страница не перенаправляется должным образом, как решить