OnClick fires for another buttons click event

Viewed 654

For some reason, in my code the onClick event for the disableAccount button gets activated whenever I click the cancel button. So cancel sets isEditMode to false but disableAccount sets it back to true thus preventing me from seeing any noticeable change in my UI. This is a next.js project.

import React from 'react';
import TextInput from '../Input/TextInput';
import SmallButton from '../Buttons/SmallButton';

const DisableAccountForm = ({t, tc, onSubmit, password, setPassword, isEditMode, setIsEditMode}) => {
  return (
    <label className="block w-full max-w-xl text-2xl mb-4">
      {t("admin")}
      <form onSubmit={onSubmit} className="w-full max-w-xl text-base mt-2">
        <p className="mb-4">
          {t("disableP")}
        </p>
        {isEditMode ?
          <div>
            <p className="mb-4">
              {t("youSureP")}
            </p>
            <p className="text-sm mb-4">
              {t("enterPasswordP")}
            </p>

          </div>
          : null
        }


        {isEditMode ? 
          <label className="flex-col">
            {tc("password")}
            <TextInput 
              placeholder={tc("password")}
              value={password}
              onChange={e=>setPassword(e.target.value)}
              style="m-1"
              required
            />
            <SmallButton 
              type="submit"
              label={tc("submit")}
              color='bg-blue-500' 
              textColor='text-white'
            />
            <SmallButton 
              type="button"
              label={tc("cancel")}
              color='bg-red-500' 
              textColor='text-white'
              onClick={() => {console.log("cancel"); setIsEditMode(false)}}
            />
          </label> 
          :
          <SmallButton 
            type="button"
            label={t("disableAccount")}
            color='bg-blue-500' 
            textColor='text-white'
            onClick={() => {console.log("why"); setIsEditMode(true)}}
          />
        }
      </form>
    </label>
  );
}

export default DisableAccountForm;

This is the controller for the code above.

import React, {useState} from 'react';
import DisableAccountForm from '../components/DisableAccountForm/DisableAccountForm';

const DisableAccountController = ({t, tc, onSubmit}) => {
  const [password, setPassword] = useState("");
  const [isEditMode, setIsEditMode] = useState(false);

  const disableAccount = (e) => {
    e.preventDefault();
    console.log("disableAccount");
    setIsEditMode(false);
  }

  return (
    <DisableAccountForm 
    t={t}
    tc={tc}
    onSubmit={disableAccount}
    password={password}
    setPassword={setPassword}
    isEditMode={isEditMode}
    setIsEditMode={setIsEditMode}
    />
  );
}

export default DisableAccountController;

SmallButton

import React from 'react';

const SmallButton = ({type, label, color, textColor, style, onClick}) => {
  return (
    <button 
      type={type} 
      className={`m-1 ${color} hover:ring-2 rounded-md px-2 py-1 min-w-20 ${textColor} ${style}`}
      onClick={onClick}
    >
      {label}
    </button>
  );
}

export default SmallButton;
1 Answers

TL;DR

The problem is that the label wrapper "clicks" the Disable account button right after the form is switched from edit to display mode. It is the default behaviour of HTML label elements. You can solve it by changing the label to div or fragment or any other wrapper that does not click your buttons.

Explain In Details

Go to the sandbox you have posted. It opens in display mode (not edit mode). Now, press the text "Admin" on the top. As you can see, the "Disable account" button is pressed - surely not the expected behaviour.

This unexpected behaviour happens for the same reason that the cancel button switches back to edit mode. the whole problem is about the label that wraps the form. The HTML native label element attaches the click event of one of the clickable elements nearby or inside the label to the content inside the label. In-display mode, the label detect the "Disable account" button and attach its click event to the whole component.

So, when you click the "cancel" button - the component switches to edit mode, and the label wrapper "clicks" the "Disable account" button and switches it back to edit mode. You don't see it when submitting the form, because you prevent the label's default behaviour in onSubmit callback. A partial solution for your problem can be to preventDefault in the cancel callback too - but it will leave the texts clickable. Another solution might be to change the label to div or fragment or any other wrapper that does not click your buttons.

Related