React event delegation - unexpected redirection to the root path

48 views Asked by At

I wrote a React component below and observed weird behavior. All of Button-B,C,D works as expected, executing the function specified in the onClick, but only the Button-A do not. If I click Button-A, it redirects me to the root path without any output to the console. Could someone help me to understand what is happening here and how I can make Button-A work, meaning assign the click event to a function I defined in the component? I assume this relates to the React event delegation but could not identify the root cause and how to solve. Thanks in advance.

import React, { useState, useEffect } from 'react'
import uuid from "react-uuid"

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSquarePlus } from '@fortawesome/free-solid-svg-icons'
import "./Create.css"

function CreateOld() {
  const [tickers, setTickers] = useState([{id: uuid(), ticker: "", ratio: ""}]);

  const addTicker = () => {
    console.log("add ticker pressed")
    const newTicker = {
      id: uuid(),
      ticker: "",
      ratio: "",
    }
    setTickers([...tickers, newTicker])
    console.log(tickers);
  }

  const onUpdateTicker = (updatedTicker) => {
    const updatedTickersArray = tickers.map((ticker) => {
      if (ticker.id === updatedTicker.id){
        return updatedTicker;
      } else {
        return ticker;
      }
    });
    setTickers(updatedTickersArray);
  }

  const onEditTicker = (ticker, key, value) => {
    onUpdateTicker({
      ...ticker,
      [key]: value,
    })
  };

  const clickHandlerA = () => {
    console.log('Clicked Button-A');
  };

  const clickHandlerB = () => {
    console.log('Clicked Button-B');
  };

  const clickHandlerC = () => {
    console.log('Clicked Button-C');
  };

  const clickHandlerD = () => {
    console.log('Clicked Button-D');
  };

  return (
    <div>
      <div>

        <div>
          <button type="button" className='createButton' id="button-A" onClick={clickHandlerA}>
            Button-A
          </button>
        </div>

        <div>
          {errorMessage && <div className='error-message'>{errorMessage}</div>}
        </div>

        <div>
        {tickers.map((ticker,index)=>(

            <div className="tickerContainer" key={ticker.id} id={ticker.id}>
              <div className="inputPortfolio">
                <div>Ticker</div>
                <input type="text" placeholder='Ticker' value={ticker.ticker} onChange={(e) => onEditTicker(ticker, "ticker", e.target.value)}/>
              </div>
              <div className="inputPortfolio">
                <div>Unit/Unit Ratio</div>
                  <input type="text" placeholder='Unit/Unit Ratio'  value={ticker.ratio} onChange={(e) => onEditTicker(ticker, "ratio", e.target.value)}/>
              </div>
            </div>
          
          ))}
        </div>

        <div className="addTickerButton">
        <button type="button" className='createTickerButton' id="addTickerButton" onClick={addTicker}>
          <FontAwesomeIcon icon={faSquarePlus} />Button-B
        </button>
      </div>
      <div>
        <button type="button" className='createButton' id="button-B" onClick={clickHandlerC}>
          Button-C
        </button>
      </div>
      </div>

      <div className="createPortfolioButton">
        <button type="button" className='createButton'  id="createPortfolioButton" onClick={clickHandlerD}>
          Button-D
        </button>
      </div>
    </div>
  )
}

export default CreateOld

I expected to execute clickHandlerA when I clicked Button-A. Also, I tried to add e.preventDefault() and e.stopPropagation() but none of them did not solve the problem.

Parent Component App.jsx

import Nabvar from './components/Navbar'
import Home from './components/Home'
import Portfolio from './components/Portfolio'
import NewPortfolio from './components/NewPortfolio'

import { BrowserRouter as Router, Routes, Route } from "react-router-dom"


function App() {  
  return (
    <div>
      <Router>
        <Nabvar />
        <Routes>
          <Route path="/" element={<Home/>}></Route>
          <Route path="/new" element={<NewPortfolio/>}></Route>
          <Route path="/portfolio/:portfolio_id" element={<Portfolio/>}></Route>
        </Routes>
      </Router>
    </div>
  )
}

export default App
1

There are 1 answers

2
adsy On

It's probably because of (ancient) HTML5 semantics around forms. I'm guessing some parent component wraps all of this in a <form>. The first button inside a form is assigned as the submit button by default in HTML5. When you submit a form, the onSubmit is called on that <form>. If no such handler is present, it does the browser default which is to POST the form contents to the current URL. It'll also do that if there is a handler but there's no e.preventDefault().

Your code doesn't show the parent, but you should add an onSubmit prop to the form if there isn't one and then call e.preventDefault() in that handler. If there already is one, add the e.preventDefault() to the top of it.