React form not submitting when using button nested inside Link tag - reactjs

In my app I have an order form with a button to "complete order" at the bottom. What I want the button to do is submit the form and push the user to the confirmation page where their order will be displayed. Right now I can do one or the other, but can't figure out how to to both.
My form component:
<form onSubmit={onSubmit}>
<label>Name:
<input
type="text"
name="name"
placeholder="Name"
value={values.name}
onChange={onChange}
/>
</label>
<Link to="/confirmation" >
<button>Complete Order</button>
</Link>
</form>
My onSubmit function:
const formSubmit = () => {
const newOrder = {
name: formValues.name.trim(),
}
setOrder(order.concat(newOrder));
setFormValues(initialFormValues);
}
I can tell my formSubmit function submit isn't working because my formValues don't reset after form submission. If I remove the Link tag and just have the button, they do, but then I don't get the redirect to the confirmation page. Any suggestions on what I can do?

If you want to update/reset the form state then navigate to the "/confirmation" path, you can't use a Link and a button/submit the form at the same time.
Remove the Link component and issue an imperative navigation after enqueueing the state updates. Don't forget to prevent the default form action from occurring so the app isn't reloaded.
import { useNavigate } from 'react-router-dom';
...
const navigate = useNavigate();
...
const onSubmit = (e) => {
e.preventDefault();
const newOrder = {
name: formValues.name.trim(),
};
setOrder(order.concat(newOrder));
setFormValues(initialFormValues);
navigate("/confirmation");
}
...
<form onSubmit={onSubmit}>
<label>Name:
<input
type="text"
name="name"
placeholder="Name"
value={values.name}
onChange={onChange}
/>
</label>
<button type="submit">Complete Order</button>
</form>

Related

Trigger "tick box" message in checkbox field

I am using React, and I would like to find a way to trigger this message (that now triggers only when pressing the type="submit" button, on will, is there any action I can use to trigger this message at any point (for example if a user presses any other button)
<input
id="conditions"
type="checkbox"
name="conditions"
ref={acceptConditions}
required
></input>
This can be achieved with reportValidity() MDN documentation
setTimeout(() =>
document.getElementById('example').reportValidity(),
3000
)
<input id="example" type="checkbox" required />
Ok here are a few approaches, that works.
approach 1
Using the reportValidity()
This function will check the validity of the element and then trigger the event.
import { useRef } from "react";
import "./styles.css";
export default function App() {
const acceptConditions = useRef();
const formRef = useRef();
const handleSubmit = () => {
acceptConditions.current.reportValidity();
};
return (
<form ref={formRef}>
<input
id="conditions"
type="checkbox"
name="conditions"
ref={acceptConditions}
required
></input>
<span>Tick this box to continue</span>
<br />
<button type={"submit"}>submit the form</button>
<br />
<button onClick={handleSubmit}>Imposter button</button>
</form>
);
}
This is a good apporach And one that i recommend.
approach 2:
First of all if you want any other button to trigger the same event then you can do is make a reference to the form and then submit the form manually on the button press.
Here is an example.
import { useRef } from "react";
import "./styles.css";
export default function App() {
const acceptConditions = useRef();
const formRef = useRef();
const handleSubmit = () => {
console.log(formRef.current.submit);
};
return (
<form ref={formRef}>
<input
id="conditions"
type="checkbox"
name="conditions"
ref={acceptConditions}
required
></input>
<span>Tick this box to continue</span>
<br />
<button type={"submit"}>submit the form</button>
<br />
<button onClick={handleSubmit}>Imposter button</button>
</form>
);
}
in the above code the imposter button will trigger the same action as the button with the type="submit".
thank you.

Validation for Input form component React

I have a form component that include multiple TextFields and each one have different validation and error. Assume my TextField had custom validation prop that will take some conditions and show validation either onChange or onBlur for each TextField. I also have custom error prop that display message without condition.
const Address = ({
onChange,
required,
error,
value = {},
validation = undefined,
}: AddressProps) => {
const validator = {
validationAddress: (value: string) => (value.length === 0) ? 'Address is required' : '',
validationCity: (value: string) => (value.length === 0) ? 'City is required' : '',
}
const handleChange = (event) => {
...
}
return (
<React.Fragment>
<TextField
id="address"
label="address"
type="text"
required={required}
onChange={handleChange}
value={value.address}
validationEvent="change"
validation={validator.validationAddress}
/>
<TextField
id="city"
label="city"
type="text"
required={required}
onChange={handleChange}
value={value.city}
validationEvent="change"
validation={validator.validationCity}
/>
</React.Fragment>
export default Address;
After that I will implement my Address component in an App with submit button.
const App = () => {
const handleSubmit = () => {
...
}
return (
<Address />
<button type='submit' onClick={handleSubmit}>
Submit
</button>
)
}
export default App;
I saw many examples that input and form put together with submit button but I am struggling with handle them separately this way. My task is need to handle validation in Address component but have submit button as an optional option outside the Address component. My goal is validation message should not show until Submit button is clicked.
Try this:
move validator to parent component
pass validatorMessage useState var to Address component
call the validator by the click of submit button
Now the message is managed in the parent component and you control when the submit button displays it.
I would suggest to consider a different approach: there is no need to manually implement validation, we can use the browser built-in validation feature:
https://medium.com/p/491327f985d0
Essentially I use standard HTML mark-up and properties.
Implementation example:
export default function App() {
const onSubmit = (data: FormData) => {
console.log(Object.fromEntries(data.entries()));
};
return (
<div className="App">
<Form onSubmit={onSubmit}>
<h2>A simple form</h2>
<TextInput label="Name:" id="name" name="name" required />
<TextInput
label="Email:"
id="email"
type="email"
name="email"
required
/>
<TextInput label="Address:" id="address" name="address" />
<TextInput
label="Tel:"
id="tel"
name="tel"
type="tel"
pattern="((\+44(\s\(0\)\s|\s0\s|\s)?)|0)7\d{3}(\s)?\d{6}" // don't rely on this
/>
<button>Submit</button>
</Form>
</div>
);
}
The Form component itself is very simple:
<form
action={action}
onSubmit={handleSubmit}
noValidate
className={style.form}
>
<div className={style.wrapper}>{children}</div>
</form>
It sets the noValidate attribute to prevent the default error notification to pop up.
The input boxes are wrapped in the form element.
The form element has an onSubmit event handler: it gets triggered when user clicks on the submit button OR when the user hit return using the keyboard.
At that point we use e.preventDefault(); to prevent default form submission and handle it manually.

React - how to target value from a form with onClick

New to react and currently working on a project with a backend.
Everything functions correctly apart from targeting the value of user selection.
basically whenever a user enters a number the setId is saved properly to the const with no problems while using the onChange method.
this method would render my page every change on text.
I am trying to save the Id only when the user clicks the button. however,
event.target.value does not work with onClick.
I tried using event.currentTarget.value and this does not seem to work.
Code:
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
Handle Submit:
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
is there a way of doing this with onclick? since I wouldn't like my component to render on every typo and only once a user has clicked the button.
Componenet:
interface GetOneCompanyProps {
company: CompanyModel;
}
interface RouteParam {
id: any;
}
interface CompanyById extends RouteComponentProps<RouteParam> {
}
function GetOneCompany(): JSX.Element {
const [id, setId] = useState('4');
const [company, setCompany] = useState<any>('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
async function send() {
try {
const response = await axios.get<CompanyModel>(globals.adminUrls.getOneCompany + id)
store.dispatch(oneCompanyAction(response.data));
console.log(response);
const company = response.data;
setCompany(company)
} catch (err) {
notify.error(err);
}
}
useEffect(() => {
send();
}, [id]);
return (
<div className="getOneCompany">
<h1>hi </h1>
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
<div className="top">
</div>
<br/>
Company: {id}
<br/>
Client Type: {company.clientType}
<br/>
Company Name: {company.name}
<br/>
Email Adress: {company.email}
<br/>
</div>
);
}
export default GetOneCompany;
Hope I am clear on this.
Thanks.
You can turn your input from being a controlled input to an uncontrolled input, and make use of the useRef hook. Basically, remove most of your attributes from the input element, and grab the current value of the input form on click of the button. From there, you can do whatever you want with the input value.
const inputRef = useRef()
...other code
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input type="number" ref={inputRef} />
<button value={id} type="button" onClick={() => console.log(inputRef.current.value)}>Search</button>
</form>
...other code
I'm afraid to say that here onChange is mandatory as we also are interested in the value which we set by setId. onClick can't be used as we can't set the value in the input.
Hope I'm clear.
Thankyou!

ReactJS On submit change classes of button and input field

I have a form and when I "Submit" the form I want to add an attribute and some extra classes to the "submit" button and the input field
This is my handleSubmit function
handleSubmit = event => {
event.preventDefault();
const formData = new FormData(event.target);
axios.post(`MyPostUrl`,formData)
.then(res => {
})
}
This is my form
<form onSubmit={this.handleSubmit} method="POST">
<div className="form-row">
<input required min="1" max="10" name="grade" className="form-control col-md-5" type="number" />
<button className="btn btn-outline-primary col-md-6">
Grade
</button>
</div>
</form>
So in let's say jQuery i could just go $(this).find("someClass") and do what ever i need to do with it. How can i achieve this with React?
What I'm trying to do is change the input class to col-md-12 and add an disabled attribute and I want to remove the button on submit
And I have a lot of forms since I've mapped over an object
Consider an example like this: https://codesandbox.io/s/throbbing-bird-ob89o
The idea is to use your component-state to control what classes, styles and attributes to use for your markup.
In this case, we define a submitted state and depending on its Boolean-value, we can use ternary operators to toggle the code we want to render.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
grade: "",
submitted: false
};
handleSubmit = e => {
e.preventDefault();
this.setState({
submitted: true
});
};
handleOnChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
const { submitted, grade } = this.state;
return (
<form onSubmit={this.handleSubmit} method="POST">
<div className="form-row">
<input
required
onChange={this.handleOnChange}
min="1"
max="10"
name="grade"
className={`form-control ${submitted ? "col-md-12" : "col-md-5"}`}
value={grade}
type="number"
disabled={submitted}
/>
{!submitted ? (
<button className="btn btn-outline-primary col-md-6">Grade</button>
) : (
""
)}
</div>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When you submit the form, we toggle the submitted state to true. Our component re-renders and that recalculates all the ternary operators in our mark-up like ${submitted ? "col-md-12" : "col-md-5"} and etc.
You would have to use react states for managing classes too.
e.g:
<button className={this.state.buttonClass}>
Grade
</button>
Better yet, create a wrapper component around it so that these actions can be controlled via props *e.g disabled={true} would add class

How to bind two methods simultaneously in reactjs

I have two components - one is App.js and other is Login.js and i have placed an input field and button in the Login and methods are placed in App.js. So i want to bind those methods by calling the Login component from the App.js.
Kindly review the logic in Login> tags as whenever i click on Flick button , it consoles the value for a second and then page refreshes automatically.
App.js
handleFlick(e){
console.log("Oho");
e.preventdefault()
}
handleChange(e) {
this.setState({
name: e.target.value
})
render() {
return (
<div>
<h1>{this.state.name} </h1><br></br>
<p>first component</p>
<Login
handleChange={this.handleChange.bind(this)}
handleFlick={this.handleFlick.bind(this)}
></Login>
Login.js
<div>
<form>
<input type="text" placeholder="enter name" onChange={(e) => { this.props.handleChange(e) }}></input>
<button onClick={(e) => { this.props.handleFlick(e) }}>Flick</button>
</form>
</div>
Change this:
<button onClick={(e) => { this.props.handleFlick(e) }}>Flick</button>
For this:
<button onClick={(e) => { this.props.handleFlick(e); e.preventDefault() }}>Flick</button>
type submit will by default refresh the page when sended

Resources