Pass input values with React-Final-Form to Axios POST - reactjs

I have a login page.
Fields: username and password.
On form submit I want to pass the 2 input values to a function which will handle a POST call via Axios API.
Because I am using React-Final-Form, I don't see anyone using refs for the inputs in order to collect the data.
Final-Form provides values as props, but I can't pass them to my external handleClickSignIn function.
const handleClickSignIn = () => {
axios.post(url, data, {
headers: headers
})
.then(response => console.log(response))
.catch(err => console.log(err))
}
const focusOnError = createDecorators();
const SignIn = () =>
<Fragment>
<h1>Sign In page</h1>
<Form
onSubmit={handleClickSignIn}
decorators={[focusOnError]}
>
{
({
handleSubmit,
values,
submitting
}) => (
<form onSubmit={handleSubmit}>
<Field
name='email'
placeholder='Email'
validate={required}
>
{({ input, meta, placeholder }) => (
<div className={meta.active ? 'active' : ''}>
<label>{placeholder}</label>
<input {...input}
type='email'
placeholder={placeholder}
className="signin-field-input"
/>
etc...

you should look at the documentation
onSubmit is a function that will be called with the values of your form
So handleClickSignIn will retrieve the form values in its first argument
const handleClickSignIn = (values) => {
// do what you want with the form values
axios.post(url, values, {headers: headers})
.then(response => console.log(response))
.catch(err => console.log(err))
}

Related

Leadspedia Form Submission does not work with onSubmit

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?

How to bind an input's value to a link in react

I want to create a simple application where you can search for images. The application is written in React using fetch (I can't use axios) and Unsplash API. My current attempt renders a list of images with a static value "cars" into the link as shown: https://api.unsplash.com/search/photos?query=**cars**
In the code example below I am using a variable "${query}" to be able to search for images but it toes not work. I need help to figure out how to fix that. Thanks in advance!
code:
import React from "react";
import { useState, useEffect } from "react";
export default function App() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [query, setQuery] = useState("");
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(
`This is an HTTP error: The status is ${response.status}`
);
}
return response.json();
})
.then((actualData) => {
console.log(actualData);
setData(actualData.results);
setError(null);
})
.catch((err) => {
setError(err.message);
setData(null);
});
}, []);
return (
<div>
{/* onSubmit={this.handleSubmit} */}
<form>
<label>
<input
placeholder="Search"
type="text"
// value={this.state.value}
// value="cars"
onChange={(e) => setQuery(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
{data &&
data.map(({ id, description, urls }) => (
<img key={id} alt={description} src={urls.regular} />
))}
</div>
);
}
I think you want to achieve conditionally firing an effect
Example
useEffect(() => {
// This will execute whenever 'query' variable changes.
}, [ query ]);
// You can bind the state using the 'value' attribute.
<input
placeholder="Search"
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
I did not quietly get the question but I think you want to do the search every time the input is changed, hence I recommend using an input instead of the form and adding "query" to the "useEffect" conditions:
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
// here
}, [query]);
<input
placeholder="Search"
type="text"
onChange={(e) => setQuery(e.target.value)} />

Formik onSubmit - remove form and success message

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.

React-Final-Form how to pass props location.search to function

I've been using React-Final-Form for the last few days but I have a lot of issues.
In my main function, PasswordReset, I need the take the prop 'location.search' and pass it to the custom 'handleSubmitOnClick' in order to handle the outcome on submit.
Here the main function:
const handleSubmitOnClick = ({ // I need the location.search to be passed here as prop
password,
password_confirmation,
}) => {
const url = 'http://localhost:3000/api/v1/users/passwords';
const headers = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
}
}
const data = {
"user": {
"reset_password_token": location.search,
"password": password,
"password_confirmation": password_confirmation,
}
}
axios.post(url, data, headers)
.then(response => console.log(response))
.catch(error => console.log('error', error)))
}
const PasswordReset = ({
location //<==== I need to pass this to 'handleSubmitOnClick' function
}) =>
<Fragment>
<h1>Password Reset page</h1>
<Form
onSubmit={handleSubmitOnClick}
decorators={[focusOnError]}
>
{
({
handleSubmit,
values,
submitting,
}) => (
<form onSubmit={handleSubmit}>
<Field
name='password'
placeholder='Password'
validate={required}
>
{({ input, meta, placeholder }) => (
<div className={meta.active ? 'active' : ''}>
<label>{placeholder}</label>
<input {...input}
type='password'
placeholder={placeholder}
className="signup-field-input"
/>
{meta.error && meta.touched && <span className="invalid">{meta.error}</span>}
{meta.valid && meta.dirty && <span className="valid">Great!</span>}
</div>
)}
</Field>
<Field
name='password_confirmation'
placeholder='Confirm password'
validate={required}
>
{({ input, meta, placeholder }) => (
<div className={meta.active ? 'active' : ''}>
<label>{placeholder}</label>
<input {...input}
type='password'
placeholder={placeholder}
className="signup-field-input"
/>
{meta.error && meta.touched && <span className="invalid">{meta.error}</span>}
{meta.valid && meta.dirty && <span className="valid">Great!</span>}
</div>
)}
</Field>
<button
type="submit"
className="signup-button"
disabled={submitting}
>
Submit
</button>
</form>
)}
</Form>
</Fragment>
export default PasswordReset;
Any help is REALLY appreciated. A bad answer is better than no answers. Thanks in advance.
You can curry your function to have location updated everytime.
Currying method: (preferred by linters)
const handleSubmitOnClick = (location) => ({ //location would come from PasswordReset every time there's a re-render
^^^^^^^^
password,
password_confirmation,
}) => {
...
}
const PasswordReset = ({
location //<==== I need to pass this to 'handleSubmitOnClick' function
}) =>
<Fragment>
<h1>Password Reset page</h1>
<Form
onSubmit={handleSubmitOnClick(location)} // <--- This will update location on every re-render
decorators={[focusOnError]}
>
{ ... }
</Form>
</Fragment>
export default PasswordReset;
Inline function method:
Alternatively you can use what other answer provided but you still need to update your handleSubmitOnClick function to accept your location prop. It will create new function on every re-render, but because inline functions are bad practice deemed by linters I prefer currying method.
<Form
onSubmit={() => handleSubmitOnClick(location)} // <--- This will create new function on every re-render
decorators={[focusOnError]}
>
<Form
onSubmit={() => handleSubmitOnClick(location)}
decorators={[focusOnError]}
>
Wrap it in an anonymous function that once called, calls your function with the required param, which would be location in this case.
After that the function would have an extra argument:
handleSubmitOnClick = location => ({..props})

Event code is not executed after default page reload

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.

Resources