Material-ui: [Link] When set to component="button" defaults to type="submit"

Created on 26 Jun 2020  路  4Comments  路  Source: mui-org/material-ui

  • [x] The issue is present in the latest release.
  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 馃槸


Link element fires up onSubmit form event when rerenders.

Expected Behavior 馃

Not firing up the onSubmit event

Steps to Reproduce 馃暪

import React, { useState } from 'react'
import {
  Button,
  TextField,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Link
} from '@material-ui/core'
import { useSnackbar } from 'notistack'
import { AuthActions } from '../../actions'
import { connect, ConnectedProps } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import { useForm } from 'react-hook-form'

const mapDispatchToProps = {
  login: AuthActions.login,
  register: AuthActions.register
}

const connector = connect(null, mapDispatchToProps)

type AuthDialogProps = ConnectedProps<typeof connector> & {
  open: boolean
  onClose: () => void
}

type FormData = {
  login: string
  email: string
  password: string
}

const emailRegExp = /^(?:(?:[^<>()\[\]\\.,;:\s@"]+(?:\.[^<>()\[\]\\.,;:\s@"]+)*)|(?:".+"))@(?:(?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(?:(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/

const AuthDialog = ({
  open,
  onClose,
  login: loginAction,
  register: registerAction
}: AuthDialogProps) => {
  const [registered, setRegistered] = useState(false)
  const [error, setError] = useState('')

  const { enqueueSnackbar } = useSnackbar()
  const { register, handleSubmit, errors, clearError } = useForm<FormData>()

  return (
    <Dialog open={open} onClose={onClose} aria-labelledby="form-dialog-title">
      <form
        onSubmit={handleSubmit(async ({ login, email, password }, e) => {
          if (registered) {
            try {
              const action = await loginAction({ login, password })
              unwrapResult(action)
              enqueueSnackbar('Logged in successfully')
              onClose()
            } catch (e) {
              setError(e.message)
            }
          } else {
            const action = await registerAction({ email, login, password })
            try {
              unwrapResult(action)
              enqueueSnackbar(
                'Registered successfully, confirm your account in a message we sent to the email'
              )
              onClose()
            } catch (e) {
              setError(e.message)
            }
          }
        })}
      >
        <DialogTitle id="form-dialog-title">{registered ? 'Log in' : 'Register'}</DialogTitle>
        <DialogContent>
          {!registered && (
            <TextField
              autoFocus
              margin="dense"
              name="email"
              label="Email Address"
              onChange={() => setError('')}
              type="email"
              error={!!(errors.email || error)}
              helperText={error ? error : errors.email && 'Wrong email format'}
              inputRef={register({ required: true, pattern: emailRegExp })}
              fullWidth
            />
          )}
          <TextField
            autoFocus
            margin="dense"
            name="login"
            label="Login"
            type="login"
            onChange={() => setError('')}
            error={!!(errors.login || error)}
            helperText={error ? error : 'Login needs to have 3-20 symbols'}
            inputRef={register({ required: true, minLength: 3, maxLength: 20 })}
            fullWidth
          />
          <TextField
            autoFocus
            margin="dense"
            name="password"
            label="Password"
            type="password"
            error={!!errors.password}
            helperText="Password needs to have 6-20 symbols"
            inputRef={register({ required: true, minLength: 6, maxLength: 20 })}
            fullWidth
          />
          <Link
            component="button"
            variant="body2"
            onClick={() => {
              clearError('login')
              clearError('password')
              clearError('email')
              setRegistered(!registered)
              setError('')
            }}
          >
            {registered ? 'Not registered? Register now.' : 'Already registered? Login now.'}
          </Link>
        </DialogContent>
        <DialogActions>
          <Button onClick={onClose} color="primary">
            Cancel
          </Button>
          <Button color="primary" type="submit">
            {registered ? 'Login' : 'Register'}
          </Button>
        </DialogActions>
      </form>
    </Dialog>
  )
}

export default connector(AuthDialog)

Context 馃敠

I'm trying to achieve seamless register-login form transition, but everytime I switch from register back to login, form fires up onSubmit event, when I removed component out of Link, everything worked fine

Your Environment 馃寧

| Tech | Version |
| ----------- | ------- |
| Material-UI | v4.?.? |
| React | v16.9.7 |
| Browser | x |
| TypeScript | v3.7.5 |
| etc. | |

Link question

Most helpful comment

Yeah we don't add a type to button in Link which defaults to submit. <Link component="button" type="button" /> would work.

But I don't think we want to overload the Link/Button choice even more. You're want a <button> so why not use a <Button>? Mixing styles with semantics can be very disorienting.

All 4 comments

Thanks for the issue.

Could you create a codesandbox using https://material-ui.com/r/issue-template and explain the steps that reproduces this issue? It's not clear if this is an issue with the other libraries, Material-UI or the integration.

Link element fires up onSubmit form event when rerenders.

How would you know that from the code? Link doesn't have an onSubmit handler.

How would you know that from the code? Link doesn't have an onSubmit handler.

Yeah, you're totally right, but I'm using it inside <form>, and for some reasons, when the component is button, it triggers form's onSubmit, but when it's not a button, it doesn't

abcd

https://codesandbox.io/s/priceless-forest-zgde2

Yeah we don't add a type to button in Link which defaults to submit. <Link component="button" type="button" /> would work.

But I don't think we want to overload the Link/Button choice even more. You're want a <button> so why not use a <Button>? Mixing styles with semantics can be very disorienting.

Ok, thanks for clearance!

Was this page helpful?
0 / 5 - 0 ratings