On onSubmit form event I'd like to send some data to the server with PUT or POST method and then refresh the page, but page reloads without executing the rest of the event code. Adding line event.preventDefault() fixes the issue but blocks reloading. What am I missing?
Event code:
handleFormSubmit = (event, requestType, articleID) => {
const title = event.target.elements.title.value;
const content = event.target.elements.content.value;
switch ( requestType ) {
case 'post':
return axios.post('http://127.0.0.1:8000/api/', {
title: title,
content: content
})
.then(res => console.log(res))
.catch(error => console.err(error));
case 'put':
return axios.put(`http://127.0.0.1:8000 /api/${articleID}/`, {
title: title,
content: content
})
.then(res => console.log(res))
.catch(error => console.err(error));
}
}
Form code:
<Form onSubmit={(event) => this.handleFormSubmit(
event,
this.props.requestType,
this.props.articleID )}>
<FormItem label="Title" >
<Input name="title" placeholder="Put a title here" />
</FormItem>
<FormItem label="Content" >
<Input name="content" placeholder="Enter some content ..." />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit">{this.props.btnText}</Button>
</FormItem>
</Form>
You need to programmatically reload your page after the form has been submitted successfully, or show an error message when there is no response or any server error:
class App extends React.Component {
handleSubmit = e => {
e.preventDefault();
return axios
.post("https://reqres.in/api/login", {
email: "title",
password: "content"
})
.then(res => {
alert(res.data.token);
location.reload();
})
.catch(error => console.log(error));
};
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Email" />
<input type="password" placeholder="Password" />
<button type="submit">Submit</button>
</form>
</div>
);
}
}
Here is a demo :https://codesandbox.io/s/j7j00nq705
You can use event.preventDefault(). But you should use react-router library history.push() to manually load another component after your request completes.
case 'post':
return axios.post('http://127.0.0.1:8000/api/', {
title: title,
content: content
})
.then(res => {
console.log(res);
// using "history.push()" to load another component
this.props.history.push('/somePage')
})
.catch(error => console.err(error));
Your above Form component must be routed using react-router or Form component must be in a Component routed using react-router.
Related
I have a form in React that I'm trying to submit to Leadspedia, but I'm seeing strange behavior. The instructions from Leadspedia API shows an example of using the method and action options to send the form. I'd like to use the onSubmit event handler to have more control of the form, but for some reason that returns with an error. Using their example submits correctly. Here is my code:
const postData = async (url = '', data = {}) => {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(data),
});
return response.json();
}
const handleSubmit = async (e) => {
e.preventDefault();
const url = "*leadspedia end point*";
const data = formValues;
postData(url, data)
.then((data) => {
console.log(data)
})
.catch(error => {
console.log(error)
})
resetForm(
Here is my form:
<form
style={{ width: '100%'}}
onSubmit={handleSubmit}
id="lp_form"
action="*leadspedia endpoint*"
method="post"
>
<div>
{formSections?.[formStep]?.fields?.map((field, index) => (
renderInput(field, index)
))}
{Object.keys(formValues).map((key, index) => (
<input key={index} type="hidden" name={key} value={formValues[key]} />
))}
<input type="hidden" id="lp_offer_id" name="lp_offer_id" value={offerId} />
<input type="hidden" id="lp_campaign_id" name="lp_campaign_id" value={campaignId} />
<input type="hidden" id="lp_campaign_key" name="lp_campaign_key" value={campaignKey} />
</div>
<div>
{formStep === 9 && (
<Button type="submit" variant="primary">
Submit
</Button>
)}
</div>
</form>
Submitting without the handleSubmit function works perfectly fine. However, submitting the form with the handleSubmit function returns a response that says Invalid campaign key or id. I've checked the values multiple times and it's the correct key and id. Am I missing something the handleSubmit function that would cause this error?
I wrote this code for a form to collect images and text data, it runs fine with no errors but in the browser, nothing is displayed but a blank screen.
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useDropzone } from 'react-dropzone';
const Form = () => {
const { register, handleSubmit } = useForm();
const [images, setImages] = useState([]);
const { getRootProps, getInputProps } = useDropzone({
accept: 'image/*',
onDrop: acceptedImages => {
setImages(acceptedImages.map(image => Object.assign(image, {
preview: URL.createObjectURL(image)
})));
}
});
const onSubmit = async data => {
const formData = new FormData();
images.forEach(image => {
formData.append('images', image);
});
formData.append('name', data.name);
formData.append('description', data.description);
try {
const response = await fetch('http://localhost:8000/submit-form', {
method: 'POST',
body: formData
});
console.log(response);
} catch (err) {
console.error(err);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<br />
{images.map(image => (
<img key={image.name} src={image.preview} alt={image.name} style={{ width: '200px' }} />
))}
<br />
<input name="name" ref={register} placeholder="Name" />
<br />
<textarea name="description" ref={register} placeholder="Description" />
<br />
<button type="submit">Submit</button>
</form>
);
}
export default Form
I expected to see a form in the browser and at least see if it actually works but i saw none. I'm using react Dropzone and react hook form on the form. And maybe a fetch for the data.
Try to change the ref in input and textarea tag like so:
<input name="name" {...register('name')} placeholder="Name" />
<textarea name="description" {...register('description')} placeholder="Description" />
Reference: https://react-hook-form.com/get-started/
I suspect you haven't called the e.preventDefault() to prevent the default form submission. So, you may try the following:
Replace the statement:
<form onSubmit={handleSubmit(onSubmit)}>
to
<form onSubmit={handleSubmit}>
change the handleSubmit function to:
const onSubmit = e => {
e.preventDefault(); //prevent submit form
let form = e.target; //get the form obj
const formData = new FormData();
images.forEach(image => {
formData.append('images', image);
});
formData.append('name', form.name.value);
formData.append('description', form.description.value);
..............................
}
This is the first time I'm using Formik and I'm facing the following issues:
I created this form using a typescript starter provided in the Formik documentation, and it works, but I'd like to show a success message and remove the form once axios returns with status 200.
So,
1. how do I target the form reference inside of the axios call? normally that is as simple as e.target but the event seems not to be available in Formik.
2. how do I access the state of the form in Formik? to toggle the success message.
The full code is available here: https://codesandbox.io/s/throbbing-water-ffl2w
Thanks a lot in advance.
<Formik
initialValues={{
firstName: "",
lastName: "",
email: ""
}}
// initialStatus={{ // resetForm(); resets this
// sent: "nope"
// }}
onSubmit={(
values: Values,
{ setStatus, setSubmitting, resetForm }: FormikActions<Values>
) => {
axios({
method: "post",
url: "https://getform.io/f/15faef97-5703-4799-930d-c3e698c99967",
data: { email: values.email, values }
}).then(r => {
setSubmitting(false);
setStatus("sent");
//resetForm();
console.log("Thanks!");
});
}}
render={() => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field
id="firstName"
name="firstName"
placeholder="John"
type="text"
/>
<label htmlFor="lastName">Last Name</label>
<Field id="lastName" name="lastName" placeholder="Doe" type="text" />
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="john#acme.com"
type="email"
/>
<button type="submit" style={{ display: "block" }}>
Submit
</button>
</Form>
)}
/>
What I recommend is using state to control what to show in your component (for some reason I cannot save the codesandbox):
const BasicForm: React.FC<{}> = () => {
const [isSent, setIsSent] = React.useState(false);
then, the fetch callback:
.then(r =>
...
setIsSent(true);
Finally in your render function
render={({ isSubmitting, status }) =>
!isSent ?
<Form> ... </Form>:
<div>Success</div>
render is a function that gets props. I see that you use setStatus, so you can get status from props and make changes in Form Component
This is an outdated version of Formik v1.1.2 and I wouldn't recommend to use it as there are some Breaking Changes such as the render method has been deprecated and will be removed in future versions. You may want to use the current version which is v2.1.4
how do I target the form reference inside of the axios call?
Formik passes the values object along with other methods (called FormikBag) inside the onSubmit prop. You can pass these value directly to axios without the need to have your own onSubmit or onChange methods. Please note that <Formik> component has other props. that will give you pretty much full control/access for your needs. That said, I'd recommend to only use Formik state/methods to avoid any side effects or bugs of having the multiple states or handlers.
v2 General Syntax:
<Formik
initialValues={initialValues}
// Other Formik props...
onSubmit = {(Object: form values, Object: Formik Bag ) => {
// access form values...
}}
>
// Access render methods and props (children props)
{(props) => {
return (
<Form>
<Field> ...
</Form>
)
}
}
</Formik>
axios Example:
<Formik
initialValues={initialValues}
onSubmit={(values) => {
console.log(values) // Object holds your form values
axios({
method: "post",
url: "url",
data: { values }
})
})
/>
how do I access the state of the form in Formik? to toggle the success message.
You can use Formik setStatus method from FormikBag inside your onSubmit to pass your server response status, then you can access that status via children props Here is an example:
<Formik
initialValues={initialValues}
onSubmit={(values, setStatus) => {
axios({
method: "post",
url: "url",
data: { values }
})
.then(res => {
if (res.status === 200) {
// 200 means POST method response with success
// Pass your server response to Formik
setStatus({
sent: true,
msg: "Message has been sent! Thanks!"
// Pass more if you need
})
}
})
.catch(err => {
// Something went wrong
setStatus({
sent: false,
msg: `Error! ${err}. Please try again later.`
})
})
})
>
// Later in your code destructuring the children props and use it like so:
{({ status }) => (
<Form>
<Field ... />
{status && status.msg && (
<p className={`alert ${ status.sent ? "alert-success" : "alert-error"}`}>
{status.msg}
</p>
)}
<button>Submit</button>
</Form>
)}
</Formik>
I did fork your codesanbox and updated the dependencies versions/syntax in this codeSandbox Example. Please note that I'm no typescript expert.
In my react typescript app - I have a login component and a handleLoginRequest function which sets the userMessage value:
function Login() {
let [userMessage, setUserMessage] = useState("");
return (
<form className="form-inline" onSubmit={(e) => {
setUserMessage(handleLoginRequest(e))
}} >
<label className="mr-sm-2">Email address:</label>
<input type="email" className="form-control mb-2 mr-sm-2" placeholder="Enter email" id="email" />
<label className="mr-sm-2">Password:</label>
<input type="password" className="form-control mb-2 mr-sm-2" placeholder="Enter password" id="password" />
<button type="submit" className="btn btn-primary mb-2">Submit</button>
{userMessage}
</form>
);
}
Update: handleLoginRequest is a API request
const handleLoginRequest = (event: any): any => {
event.preventDefault();
const data = {
email: event.target.email.value,
password: event.target.password.value
};
axios.post(`${process.env.REACT_APP_HTTP_PROXY}/api/v1/login`, data)
.then(res => {
console.log(res.data);
return res.data;
})
.catch(err => {
console.log(err);
return err.message;
});
}
Expected behavior:
When the form is submitted, the function setUserMessage(handleLoginRequest(e)) is called. I expect that it update the value of userMessage to login request received.
Actual result:
No change in the value of userMessage
What is the fix here?
Updated answer per the question modification
The issue is stemming from the fact you're trying to setState based on a axios post request. Axios requests are asynchronous, meaning they will happen out of order with the program control flow. Therefore, to solve this issue, it is important you only make a call to setUserMessage when the request is complete.
Example Code:
axios.post(`${process.env.REACT_APP_HTTP_PROXY}/api/v1/login`, data)
.then(res => {
setUserMessage(res.data);
})
.catch(err => {
// Do Something with error response
setUserMessage("Error: Something with the request went wrong.");
});
I will link a codesandbox to demonstrate:
https://codesandbox.io/s/focused-leakey-0jer4
I am currently building a react application with a .net core back end. My current issue lies in a view that is meant to edit an article (which is made up of only a title and description). On componentDidMount, I am getting the route param id from the route and retrieving the article from the server with it (I've verified that this works correctly). My issue is that my form is not filling out with the fetched data. I'm of the understanding that since the form fields set to this.state... then they should update as the state updates however this is not what I'm seeing. I believe the issue is may lie with the warning I'm receiving in console:
index.js:2177 Warning: A component is changing a controlled input of
type hidden to be uncontrolled. Input elements should not switch from
controlled to uncontrolled (or vice versa). Decide between using a
controlled or uncontrolled input element for the lifetime of the
component.
I've read the documentation the warning points to and am not seeing how my component violates this.
My component is below in full:
import React, { Component } from 'react';
import CKEditor from 'react-ckeditor-component';
export class ArticlesEdit extends Component {
displayName = ArticlesEdit.name
constructor(props) {
super(props);
this.state = {
title: '',
description: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
updateDescription(event){
this.setState({description: event.target.value});
}
render() {
return(
<form onSubmit={this.handleSubmit} >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Title">Title</label>
<div className="col-md-4">
<input className="form-control" type="text" id="title" name="title" defaultValue={this.state.title} required />
</div>
</div >
<CKEditor activeClass="editor" content={this.state.description} events= {{"change": this.onEditorChange.bind(this) }} />
<input type="hidden" id="description" name="description" value={this.state.description} onChange={this.updateDescription}/>
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
</div >
</form >
);
}
onEditorChange(evt){
var newContent = evt.editor.getData();
this.setState({
description: newContent
});
}
handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
console.log(this.state.title);
// POST request for Add employee.
fetch('https://localhost:44360/api/articles/', {
method: 'PUT',
body: data
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/articles");
})
}
}
You are not parsing the JSON you get as response to your fetch in componentDidMount. If you add .then((response) => response.json()) it should work as expected.
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((response) => response.json())
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
You also need to use the value prop instead of the defaultValue prop on your input so that it will have the value of title in your state.
<input
className="form-control"
type="text" id="title"
name="title"
value={this.state.title}
required
/>