Странное поведение с тройным отключением одного и того же компонента кнопки в React, что приводит к отправке формы

Когда мы используем тройной код для замены кнопки с type = "button" на кнопку с type = "submit" в форме в React, замененная кнопка приводит к отправке формы.

import {useState} from "react";


function App() {
    const [isClicked, setIsClicked] = useState(false);
    const handleClick = () => {
        setIsClicked(true)
    };

    return (
        <form action='/submitted'>
            <div>
                <label htmlFor = "firstName">First Name</label>
                <input id = "firstName" name = "firstName" placeholder = "John"/>
            </div>
            <div>
                <label htmlFor = "lastName">Last Name</label>{' '}
                <input id = "lastName" name = "lastName" placeholder = "Doe"/>
            </div>
            {isClicked
                ? <button type = "submit">Submit</button>
                : <button type = "button" onClick = {handleClick}>Click Me</button>}
        </form>
    )
}

export default App

Но если мы разделим тернарный код на два условных выражения, форма не будет отправлена.

import {useState} from "react";


function App() {
    const [isClicked, setIsClicked] = useState(false);
    const handleClick = () => {
        setIsClicked(true)
    };

    return (
        <form action='/submitted'>
            <div>
                <label htmlFor = "firstName">First Name</label>
                <input id = "firstName" name = "firstName" placeholder = "John"/>
            </div>
            <div>
                <label htmlFor = "lastName">Last Name</label>
                <input id = "lastName" name = "lastName" placeholder = "Doe"/>
            </div>
            {isClicked ? <button type = "submit">Submit</button> : null}
            {!isClicked ? <button type = "button" onClick = {handleClick}>Click Me</button> : null}
        </form>
    )
}

export default App

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

import {useState} from "react";

const SubmitButton = () => <button type = "submit">Submit</button>;

function App() {
    const [isClicked, setIsClicked] = useState(false);
    const handleClick = () => {
        setIsClicked(true)
    };

    return (
        <form action='/submitted'>
            <div>
                <label htmlFor = "firstName">First Name</label>
                <input id = "firstName" name = "firstName" placeholder = "John"/>
            </div>
            <div>
                <label htmlFor = "lastName">Last Name</label>
                <input id = "lastName" name = "lastName" placeholder = "Doe"/>
            </div>
            {isClicked
                ? <SubmitButton/>
                : <button type = "button" onClick = {handleClick}>Click Me</button>}
        </form>
    )
}

export default App

React пытается сделать что-то умное, но может ли кто-нибудь объяснить, почему это происходит?

Я понимаю, что новый элемент заменяется при запуске обработчика onClick. Однако я не понимаю, почему новый элемент считается нажатым, хотя это явно не так.

В последнем примере кода вы определяете SubmitButton как простую кнопку HTML без type = "submit". Я думаю, именно поэтому в этом примере не происходит отправка.

TinouHD 17.04.2024 10:44

@TinouHD Тип элементов кнопок по умолчанию — type = "submit"; Но добавлю для ясности.

OrderAndChaos 18.04.2024 09:51

мб ты прав, спасибо, что заметил это для меня

TinouHD 18.04.2024 10:20
Поведение ключевого слова "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) для оценки ваших знаний,...
3
3
144
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

import {useState} from "react";


function App() {
    const [isClicked, setIsClicked] = useState(false);
    const handleClick = () => {
        setIsClicked(true)
    };

    return (
        <form action='/submitted'>
            <div>
                <label htmlFor = "firstName">First Name</label>
                <input id = "firstName" name = "firstName" placeholder = "John"/>
            </div>
            <div>
                <label htmlFor = "lastName">Last Name</label>{' '}
                <input id = "lastName" name = "lastName" placeholder = "Doe"/>
            </div>
            {isClicked
                ? <button key = "submit" type = "submit">Submit</button>
                : <button key = "button" type = "button" onClick = {handleClick}>Click Me</button>}
        </form>
    )
}

export default App

Ах, интересно! Я думал это только для петель.

OrderAndChaos 17.04.2024 11:50

Вы уже нашли решения, но я думаю, что вам нужно объяснение.

Причина: «Распространение событий и привязка».

Объяснение:

Представьте, что у вас есть форма с кнопкой отправки, нажатие которой должно отправить данные формы на сервер. Однако в вашем случае кнопка отправки появляется при нажатии другой кнопки. В вашем первом фрагменте кода обе кнопки уже отрисованы с помощью реакции. Разница лишь в том, что один из них отображается вам на основе isClicked. Форма привязана к кнопке отправки под капотом.

  • Здесь происходит следующее: когда вы нажимаете на первую кнопку, Событие щелчка распространяется на форму.

  • Достигнув формы, React находит кнопку отправки. которое вызвало событие щелчка (которое уже обнаруживается, когда вы нажал на другую кнопку) и находит его.

  • Следовательно, это вызывает отправку формы.

Почему разделение условий сработало?

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

  • Поскольку на этот раз кнопка отправки не привязана к форме (когда вы использовали тройную кнопку, кнопка была там, визуализирована, привязана, но скрыта из-за условия), событие не распространяется на форму

Надеюсь, вам понравилось объяснение, это был интересный сценарий и вопрос, и чтобы понять это, потребовалось некоторое тестирование.

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

Похоже, это связано с процессом согласования React и алгоритмом сравнения: https://legacy.reactjs.org/docs/reconciliation.html

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

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

В документах React указано, что рекомендуемым средством решения этой проблемы является размещение ключей на элементах, как указано в ответе @TinouHD.

Когда у детей есть ключи, React использует их для сопоставления детей в исходном дереве с детьми в последующем дереве.

Вот почему это будет работать:

{isClicked
    ? <button key = "submit" type = "submit">Submit</button>
    : <button key = "button" type = "button" onClick = {handleClick}>Click Me</button>}

Запуск отправки при нажатии не является проблемой, специфичной для React, см. этот чистый HTML и JS:

<!DOCTYPE html>
<html>

<head>
    <meta charset = "utf-8">
    <meta name = "viewport" content = "width=device-width">
    <title>Swap button pure JS demo</title>
</head>

<body>
    <form onsubmit = "alert('submitted with swapped attribute')">
        <button type = "button" id = "button">Click me</button>
    </form>
    <script>
        document.getElementById("button").addEventListener("click", function (e) {
            e.target.setAttribute("type", "submit");
        })
    </script>
</body>

</html>

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