I'm new to using async/await and from my understanding, I can declare the function as an asynchronous function and then use await inside of that function.
What I would like for my code to do is to run document.getElementById('PrePayeezyForm').submit() only after the updateNewInvoice function has completed. UpdateNewInvoice function is setting data to my Redux store and I have a form that is referring to the data from the store.
The issue is, the last line is running so quickly that the form is referring to empty data in the store before it is set. If I use a setTimeout, it works but I don't want to have to do that. Thanks for the help.
import styles from './styles.module.scss'
const FormPayeezy = ({
payeezyArray,
removeInvoiceModal,
updateNewInvoice,
updatePO,
customer
}) => {
async function updateNewInvoices(event) {
event.preventDefault()
//save amount to redux and generate form values
let amount = event.target.x_amount.value
let customer = event.target.customer.value
await updateNewInvoice(amount, customer)
document.getElementById('PrePayeezyForm').submit();
}
function updatePOs(event) {
//save PO to redux
updatePO(event.target.value)
}
function hideModal(event) {
event.preventDefault()
removeInvoiceModal()
}
return (
<form id="PrePayeezyForm" className={styles.addInvoiceForm} method="post" action="https://demo.globalgatewaye4.firstdata.com/payment" onSubmit={updateNewInvoices}>
<input name="x_login" value={payeezyArray.login} type="hidden" />
<input name="x_invoice_num" value={payeezyArray.payment_num} type="hidden" />
<input name="x_fp_sequence" value={payeezyArray.sequence} type="hidden" />
<input name="x_fp_timestamp" value={payeezyArray.timestamp} type="hidden" />
<input name="x_fp_hash" value={payeezyArray.hash} type="hidden" />
<input name="x_show_form" value="PAYMENT_FORM" type="hidden" />
<input name="x_relay_response" value="TRUE" type="hidden" />
<label>Payment Description</label>
<input type="text" required name="loadNum" id="loadNum" onBlur={updatePOs} />
<label>Payment Amount</label>
<input type="text" required name="x_amount" id="x_amount" />
<input required name="customer" id="customer" value={customer} type="hidden" />
{payeezyArray.items}
<button className={styles.buttonAdd} type="submit"
>Pay Invoice</button>
<button className={styles.buttonCancel}
onClick={hideModal}
>Cancel</button>
</form>
)
}
export default FormPayeezy
//////////////////////////////////////////
updateNewInvoice = async (amount, customerid) => {
const { updatePayeezyCustomer, updatePayeezyAmount } = this.props
updatePayeezyAmount(amount)
updatePayeezyCustomer(customerid)
let loadid = this.props.payeezyArray.PO
let note = `New Invoice for PO #: ${loadid}`
let paymentInfo = {
'other': [
{
customerid,
note,
amount: Number(amount)
}
]
}
this.props.getPaymentResponse(paymentInfo, (id) => {
this.props.updatePayeezyPaymentNum(id)
})
let timestamp = Math.floor(Date.now() / 1000)
this.props.updatePayeezyTimestamp(timestamp)
let hash = createPayeezyHash(this.props.payeezyArray, timestamp, amount)
this.props.updatePayeezyHash(hash)
let value = `${this.props.payeezyArray.PO}<|><|>PO ${this.props.payeezyArray.PO}<|>1<|>${amount}<|>N`
let content = <input name="x_line_item" value={value} type="hidden"></input>
this.props.updatePayeezyItems(content)
}
Related
I am messing around with Riot's API that allows getting information of a player by the player's name. I am trying to get an API key (I only got a 24 hour key) and the target player's name from users' input.
export function PlayerSearch() {
function handlesubmit(e) {
console.log(e.target);
e.preventDefault();
}
return (
<div className='player'>
<div className='inputfield'>
<form onSubmit={handlesubmit} method='GET' autoComplete="off">
<div>
<label htmlFor="key">Your API key:</label>
<input
placeholder='Your API key'
onFocus={(e)=>{e.target.placeholder=''}}
type="text"
id="key"
name="key" />
</div>
<div>
<label htmlFor="name">Player name:</label>
<input
placeholder='Player name'
onFocus={(e)=>{e.target.placeholder=''}}
type="text"
id="name"
name="name" />
</div>
<div>
<input type='submit' />
</div>
</form>
</div>
</div>
);
}
And I got this in the console:
So how exactly do I extract the two inputs from the form?
In addition, is it possible that I can call another component and pass data as props to handle the submitted data so that the code is cleaner?
You should have a state to store your inputs:
const [formData, setFormData] = useState({ key: "", name: "" });
Then you need a function that gets called onChange of your input fields to update your state:
const onChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
And you need to pass that function to your input onChange property:
<input
placeholder="Your API key"
onFocus={(e) => {
e.target.placeholder = "";
}}
type="text"
name="key"
value={formData.key}
onChange={onChange}
/>
Then you can access the state in your handleSubmit:
function handlesubmit(e) {
console.log(formData);
e.preventDefault();
}
Parent Component
I have a parent component Users with the following snippet:
addUser(index, user) {
var users = this.state.users
var existingUser = users[index]
if (existingUser !== undefined) {
this.updateUser(index, user)
} else {
users.push(user)
this.setState({
users : users
})
}
}
updateUser(index, itemAttributes) {
this.setState({
travellers: [
...this.state.users.slice(0,index),
Object.assign({}, this.state.users[index], itemAttributes),
...this.state.users.slice(index+1)
]
});
}
The updateUser functionality has been taken from React: Updating state when state is an array of objects
And in JSX I have the following snippet:
...
<Form onSubmit={this._handleFormSubmit.bind(this)}>
<UserDetails index={0} callback = {this.addUser.bind(this)}/>
<UserDetails index={1} callback = {this.addUser.bind(this)}/>
<input type="submit" className="btn btn-primary mb-4" value="Continue"/>
</Form>
...
Child Component
Then in the child component UserDetails I have:
handleChange(event) {
event.preventDefault();
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({formValues})
this.props.callback(this.props.index, this.state.formValues)
}
And in JSX:
<div className="row">
<div className="col-md-4">
<FormGroup>
<Label>First Name</Label>
<Input type="text" name="firstName" placeholder="First Name" value={this.state.formValues["firstName"]} onChange={this.handleChange.bind(this)} />
</FormGroup>
</div>
<div className="col-md-4">
<FormGroup>
<Label>Last Name</Label>
<Input type="text" name="lastName" placeholder="Last Name" value={this.state.formValues["lastName"]} onChange={this.handleChange.bind(this)} />
</FormGroup>
</div>
</div>
Now I can see two forms, each with a First Name and a Last Name fields. Now the problem is when I enter the First Name for the first user, the First Name for the second user is also automatically set to that of the first user. Thus, I cannot enter separate names for the two users. How can I solve this issue?
I see that you are mutating the state directly by using
let formValues = this.state.formValues;
formValues[name] = value;
One way to alleviate this issue is to spread the formValues before assigning
let formValues = {...this.state.formValues};
or you can use Immer
I am trying to implement simple form which triggers API call on form submit in React.
It blows my mind as when trying the below code:
import React, { Component } from 'react';
import axios from 'axios';
var panelStyle = {
'max-width': '80%',
margin: '0 auto'
}
class DBInject extends Component {
constructor() {
super();
this.formHandler = this.formHandler.bind(this);
this.state = {
formFields: {Id: '',
Name: '',
Payment: "01-10-2019",
Type: '',
Value: 110,
Cycle:'',
Frequency:''
}
}
}
render() {
return(
<div>
<div class="panel panel-primary" style={panelStyle}>
<div class="panel panel-heading">React Forum - Register</div>
<div class="panel panel-body">
<form onsubmit={this.formHandler(this.state.formFields)}>
<strong>Id:</strong> <br /> <input type="text" name="Id" placeholder="123" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Id} /> <br />
<strong>Name:</strong> <br /> <input type="text" name="Name" placeholder="me#example.com" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Name}/> <br />
<strong>Cycle:</strong> <br /> <input type="text" name="Cycle" placeholder="me#example.com" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Cycle} /> <br />
<strong>Frequency:</strong> <br /> <input type="text" name="Frequency" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Frequency}/> <br />
<strong>Type:</strong> <br /> <input type="text" name="Type" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Type} /> <br />
<strong>Payment:</strong> <br /> <input type="date" name="Payment" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Payment}/> <br />
<strong>Value:</strong> <br /> <input type="number" name="Value" onChange={(e) => this.inputChangeHandler.call(this, e)} value={this.state.formFields.Value}/> <br /><br />
<button class="btn btn-primary">Send to database</button>
</form>
</div>
</div>
</div>
);
}
inputChangeHandler(e) {
console.log(e);
let formFields = {...this.state.formFields};
formFields[e.target.name] = e.target.value;
this.setState({
formFields
});
};
formHandler(formFields) {
console.log(formFields);
alert('This button does nothing.');
axios.post('http://127.0.0.1:1880/api','', {headers:formFields})
.then(function(response){
console.log(response);
//Perform action based on response
})
.catch(function(error){
console.log(error);
//Perform action based on error
});
}
}
export default DBInject
formHandler gets called every time InputchangeHandler gets called - which is not my intention.
Is there any other simpler way to do it in React?
Every time you call setState the component re-renders. Your form element is calling the function every time it re-renders. You need to make the following changes:
#Update onsubmit
<form onsubmit={this.formHandler.bind(this)}>
#Reference the formFields directly from the component's state
formHandler() {
const { formFields } = this.state;
You can read more on React forms here:
https://reactjs.org/docs/forms.html
Make sure to reference state from inside the handler function rather than passing it in - as this will make your function re-render every state update.
Form element:
//make sure the "S" in submit is capitalized
<form onSubmit={this.formHandler}>
...Your Form Contents
</form>
Handler function:
formHandler(e) {
e.preventDefault() // stop propagation at the start
// reference state from here with this.state like so:
const { formFields } = this.state;
...Your handler code
}
I am trying to make a simple component in React to fill a form, but when I run it the console show me the error too much recursion
I already try to remove the event.preventDefault() on the handleSubmitbut that didn't work, I alse try to encapsulate the form using {this.props.currentUser ? (<form>...</form>) : ""
My code is the next one:
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<label htmlFor="productName">Product name:</label>
<input
className="form-control"
type="text"
ref="productName"
required
/>
</div>
<div className="form-group">
<label htmlFor="productName">Product description:</label>
<input
className="form-control"
type="text"
ref="productDescription"
required
/>
</div>
<div className="form-group">
<label htmlFor="productName">Minimum amount increase</label>
<input
className="form-control"
type="text"
ref="minIncrease"
required
/>
</div>
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
And the handleSubmit
handleSubmit(event) {
event.preventDefault();
// Find the txt field via React ref
const productName = ReactDOM.findDOMNode(this.refs.productName);
const productDescription = ReactDOM.findDOMNode(
this.refs.productDescription
);
const minIncrease = ReactDOM.findDOMNode(this.refs.minIncrease);
Meteor.call(
"auctions.insert",
productName,
productDescription,
minIncrease
);
Auctions.insert({
productName, // product name
productDescription, // product description
minIncrease, // minimum increase
value: 0, // initial value
winner: "", // actual winner
owner: Meteor.userId, // _id of logged in user
username: Meteor.user().username, // username of logged in user
createAt: new Date() // current time
});
// Clear form
ReactDOM.findDOMNode(this.refs.productName).value = "";
ReactDOM.findDOMNode(this.refs.productDescription).value = "";
ReactDOM.findDOMNode(this.refs.minIncrease).value = "";
}
I find the error, it was that in the handleSubmit it was missing .value.trim()
The correct code is:
handleSubmit(event) {
event.preventDefault();
// Find the txt field via React ref
const productName = ReactDOM.findDOMNode(
this.refs.productName
).value.trim();
const productDescription = ReactDOM.findDOMNode(
this.refs.productDescription
).value.trim();
const minIncrease = ReactDOM.findDOMNode(
this.refs.minIncrease
).value.trim();
...
}
I have a component in which I want to display another component after form submit.
When I'm running function on form submit I'm changing submitted value to true and if I do console.log(submitted) it changes to true but in the template it is still false so Alert component doesn't show up.
I'm trying to learn hooks and maybe the problem with how I'm using them?
My component looks like this
export const SignupForm = () => {
let name:any = handleUserInput('');
let email:any = handleUserInput('');
let password:any = handleUserInput('');
let submitted:any = false;
function registerUser(event: React.FormEvent<HTMLFormElement>): void {
event.preventDefault();
submitted = true;
const registerInfo = Object.assign({}, {
email: email.value,
password: password.value,
name: name.value
});
axios.post('/register', registerInfo)
.then(response => {
errors = response.data.errors
})
.catch(error => console.log(error))
}
function handleUserInput(initialValue: string): object {
const [value, setValue] = useState(initialValue);
function handleChange(event: Event): void {
let element = event.target as HTMLInputElement;
setValue(element.value);
}
return {
value,
onChange: handleChange
}
}
return (
<div>
<div dangerouslySetInnerHTML={{__html: submitted}}></div>
{submitted ? <Alert /> : ''}
<div className="form-holder">
<form action="POST" className="form" onSubmit={(e) => registerUser(e)}>
<label htmlFor="Email">Email</label>
<input type="text" id="email" className="form__input" {...email} required />
<label htmlFor="password">Password</label>
<input type="password" id="password" className="form__input" {...password} required />
<label htmlFor="name">Name</label>
<input type="text" id="name" className="form__input" {...name} required />
<button type="submit" className="form__button">Signup</button>
</form>
</div>
</div>
);
}
submitted local variable is assigned asynchronously. This won't result in component update.
It should be:
...
const [submitted, setSubmitted] = useState(false);
function registerUser(event: React.FormEvent<HTMLFormElement>): void {
event.preventDefault();
setSubmitted(true);
...