I'm trying to integrate my submit function that sends the data gotten from the form to my django API.
I have done the form validation with formik and I have tried calling the function to post the data.
How my onSubmit function for formik looks:
const Registration = () => (
<Formik
initialValues={{
username: "",
email: "",
password: "",
re_password: "",
}}
onSubmit={(values, { setSubmitting }) => {
handleRegistration(values, this.props.history);
setSubmitting(false);
}}
>
And the function for submitting data to the server:
const handleRegistration = (e, values) => {
e.preventDefault();
fetch("http://127.0.0.1:8000/auth/users/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(values),
})
.then((resp) => resp.json())
.then((res) => {
console.log("registration res", res);
window.location.href = "/login";
})
.catch((error) => {
console.log("registration error", error);
});
};
This is the way I'm submitting with formik:
const onSubmit = (values, { resetForm, setSubmitting }) => {
const data = new FormData();
setSubmitting(true);
data.append('form', JSON.stringify({ values.firstValue, values.secondValue }));
// call post('/yourEndpoint', data)
};
Any particular reason you're using setSubmitting(false) ? I also see you have e.preventDefault(); and I'm not sure if you need that.
Related
I have a form built in react js and I am using an axios post request to register a user after form submission. I have tried to put a try catch block after the promise but i dont think i am getting passed the post request. I have imported axios and checked package json to make sure its downloaded. I have also implemented a catch block to catch errors but I am still getting
(TypeError: Cannot read properties of undefined (reading 'post'))
const handleSubmit = (e) => {
e.preventDefault();
axios.post('http://localhost:5000/api/users/register',{
name: data.name,
email:data.email,
password: data.password
})
.then((res) => {
console.log("server response:",res);
})
.catch((err) =>{
console.log("Server responded with error", err);
})
}
It is indeed correct to use axios.post in that way but you are not handling a response or an error from the server. For these axios provides you with the .then() and .catch() methods, which handle the results/error for you.
Here's an example:
(You can check the results of the post request in this example in the console tab or the network tab in the developer tools of your favorite browser).
const RegisterScreen = () => {
const [ data, setData ] = useState({
name: "",
email: "",
password: "",
});
const handleChange(e) => {
setData({...data, [e.target.name]: e.target.value});
}
const handleSubmit = (e) => {
e.preventDefault();
axios.post("YOUR/URL:5000", {
name: data.name,
email: data.email,
password: data.password
})
.then((res) => {
console.log("Server response: ", res);
})
.catch((err) => {
console.log("Server respondend with error: ", err);
})
}
}
return (
<>
<YourForm />
</>
);
}
You may have to specify the content type while sending a POST request.
const handleSubmit = (e) => {
e.preventDefault();
axios.post('http://localhost:5000/api/users/register',{
name: data.name,
email:data.email,
password: data.password
}, {
headers: {
'Content-Type': 'application/json' }
})
.then((res) => {
console.log("server response:",res);
})
.catch((err) =>{
console.log("Server responded with error", err);
})
}
On my console log, i can see my object for my response.json but i think i forget something for my setUser because my object return undefined ?
function Profil() {
const [user, setUser] = useState({});
const getUser = () => {
const headers = new Headers({
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
Authorization: "Bearer " + localStorage.getItem("token"),
});
const options = {
method: "GET",
headers: headers,
};
fetch(
"https://..../user",
options
)
.then((response) => {
return console.log(response.json());
})
.then(
(responseObject) => {
const userData = responseObject;
setUser({ ...user, userData });
console.log(user);
},
(error) => {
console.log(error);
}
);
};
useEffect(() => {
getUser();
}, []);
return (
<div>
<h1> Prénom</h1>
</div>
);
}
export default Profil;
my object on my console log is
[[PromiseResult]]: Object
email: "test#gmail.com"
firstname: "test"
lastname: "test"
_id: "61519405b8dc4a001be666"
You're returning undefined from your Promise:
.then((response) => {
return console.log(response.json());
})
response.json() itself returns a Promise, so return that:
.then((response) => {
return response.json();
})
There's no need to log the Promise itself. If you want to log the raw response JSON to the console then do that in the next Promise:
.then(
(responseObject) => {
// here:
console.log(responseObject);
const userData = responseObject;
setUser({ ...user, userData });
console.log(user);
},
(error) => {
console.log(error);
}
);
Additionally, be aware of what this is doing:
console.log(user);
This will log the state of user when this code is running. It will not reflect the update from here:
setUser({ ...user, userData });
Because that state update happens asynchronously. If you want to log the updated state value, either do it in a useEffect, or directly in the rendering of the component, or just log the object you're passing to setUser.
You also don't need your userData variable at all. It adds no value and is just a reference to responseObject.
I call a state function in my component, the function should change the state(and it does but late), i want to log the change but the log triggers before the state is changed
this is the function in the state:
const login = async (user, password) => {
const body = {
username: user,
password: password,
};
await axios
.post('/sessions', body, {
headers: { 'Content-Type': 'application/json' },
})
.then((resp) => {
dispatch({ type: LOGIN, payload: resp.data.data });
})
.catch((err) => {
console.log(err.response.data);
});
};
and this is the call in the component
const onSubmit = (e) => {
e.preventDefault();
login(user, password);
console.log(credes);
};
"credes" is the state for that response, but it keeps printing the initial state witch is an empty object
the function triggers on the form submission but logs first and updates the state later.
As pointed out by bubulledu93, ronakvp and coreyward, I was butchering the syntax. I was trying to perform two actions in one function, so I moved the log into a useEffect to watch for changes in the "credes" hope is the right way but is working as I needed it.
const login = (user, password) => {
const body = {
username: user,
password: password,
};
axios
.post('/sessions', body, {
headers: { 'Content-Type': 'application/json' },
})
.then((resp) => {
dispatch({ type: LOGIN, payload: resp.data });
})
.catch((err) => {
console.log(err.response.data);
});
};
and the call in the component + the useEffect
const onSubmit = (e) => {
e.preventDefault();
login(user, password);
};
useEffect(() => {
if (credes.success) {
console.log(credes.data);
}
}, [credes]);
There isn't any benefit to awaiting as the last call in a function. Instead of using async and await, simply return the Promise chain started by axios.post() to onSubmit and then chain on it (or use await there):
const login = (user, password) => {
const body = {
username: user,
password: password,
};
return axios
.post('/sessions', body, {
headers: { 'Content-Type': 'application/json' },
})
.then((resp) => {
dispatch({ type: LOGIN, payload: resp.data.data });
})
.catch((err) => {
console.log(err.response.data);
});
};
// Option 1:
const onSubmit = (e) => {
e.preventDefault();
login(user, password)
.then(() => {
console.log(credes);
});
};
// Option 2:
const onSubmit = async (e) => {
e.preventDefault();
await login(user, password);
console.log(credes)
}
I'm new to using both axios and formik and likely making a simple mistake here. I'm attempting to check if the user's email and password exists and has been typed correctly. Upon authentication they should be logged in with useContext and then redirected to the homepage.
Currently, after submitting, the form simply remains greyed out without reaching the .then/.catch block. Is it a problem with the way I typed the auth parameter?
const LoginForm = () => {
const authenticate = useContext(AuthContext);
const [serverState, setServerState] = useState();
const handleServerResponse = (ok, msg) => {
setServerState({ok, msg});
};
const handleOnSubmit = (values, actions) => {
axios({
method: "POST",
url: "http://localhost:5000/api/users/login",
data: values,
auth: {
email,
password
}
})
.then(response => {
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Logged In!");
})
.catch(error => {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
});
authenticate.login();
Router.push("/")
};
return (
<Formik
initialValues={{
email: "",
password: "",
}}
validationSchema={Yup.object().shape({
email: Yup.string(),
password: Yup.string(),
})}
onSubmit={handleOnSubmit}
>
{({ isSubmitting }) => (
<Form>
<Field
name="email"
type="email"
fullWidth
component={TextField}
variant="outlined"
label="Email"
/>
<Box pt={1}>
<Field
name="password"
type="password"
fullWidth
component={TextField}
variant="outlined"
label="Password"
/>
</Box>
<Box pt={2}>
<Button
type="submit"
variant="contained"
fullWidth
color="primary"
disabled={isSubmitting}
>
Submit
</Button>
{serverState && (
<Typography className={!serverState.ok ? "errorMsg" : ""}>
{serverState.msg}
</Typography>
)}
</Box>
</Form>
)}
</Formik>
);
};
The Node JS API on the backend:
const login = async (req, res, next) => {
const { email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
const error = new HttpError("Login failed, please try again later.", 500);
return next(error);
}
if (!existingUser || existingUser.password !== password) {
const error = new HttpError("Invalid credentials, login failed.", 401);
return next(error);
}
res.json({ message: "Logged in!" });
};
The reason why the form is grayed out is because you use isSubmitting and when you submit the form, it is set to true, but inside handleOnSubmit, you have an synchronous function, so you only call actions.setSubmitting(false) once the formik already think you finished the onSubmit.
Another way of doing this is to make handleOnSubmit return a promise and once the promise is resolved, formik will set isSubmitting to false automatically.
This is explained in the docs
IMPORTANT: If onSubmit is async, then Formik will automatically set isSubmitting to false on your behalf once it has resolved. This means you do NOT need to call formikBag.setSubmitting(false) manually. However, if your onSubmit function is synchronous, then you need to call setSubmitting(false) on your own.
So what I recomendo you to do and solve your problem is make handleOnSubmit an async method and return the axios call (return a promise) or use await.
An example of that would be something like
const handleOnSubmit = (values, actions) => {
// returning a promise
return axios({
method: "POST",
url: "http://localhost:5000/api/users/login",
data: values,
auth: {
email,
password
}
})
.then(response => {
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Logged In!");
})
.catch(error => {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
});
// this shouldn't be outside the .then/.catch
// if you are going to use .then/.catch, put the above line inside it
// authenticate.login();
// Router.push("/")
};
Or with async/await
// using async
const handleOnSubmit = async (values, actions) => {
// using await in the axios call
try {
const response = await axios({
method: "POST",
url: "http://localhost:5000/api/users/login",
data: values,
auth: {
email,
password
}
})
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Logged In!");
} catch(error) {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
}
authenticate.login();
Router.push("/")
};
So what I recommend you to do and solve your problem is to make handle On Submit an async method and return the await.
const handleOnSubmit = async (values, actions) => {
try {
const response = await axios({
method: "POST",
url: "http://localhost:5000/api/users/login",
data: values,
auth: {
email,
password
}
})
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Logged In!");
} catch(error) {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
}
authenticate.login();
Router.push("/")
};
I have a block of code in ReactJS with FETCH api that is working perfectly fine but when I tried to replace it with AXIOS then its not functioning perfectly, even though I checked the documentation.
WORKING CODE OF FETCH API:
const signup = (user) => {
return fetch(`${API}/signup`, {
method: "POST",
headers: {
Accept: 'application/json',
"Content-Type": 'application/json'
},
body: JSON.stringify(user)
})
.then(response => {
return response.json();
})
.catch(err => {
console.log(err);
});
}
const clickSubmit = (event) =>{
event.preventDefault();
signup({name, email, password})
.then(data => {
if(data.error){
setValues({...values, error: data.error, success: false})
}
else{
setValues({...values, name: '', email: '', password: '', error:'', success:true})
}
})
}
NOT WORKING SAME CODE BUT WITH AXIOS LIBRARY:
import axios from 'axios';
const signup = (user) => {
return axios(`${API}/signup`, {
method: "POST",
headers: {
Accept: 'application/json',
"Content-Type": 'application/json'
},
data: JSON.stringify(user)
})
.then(response => {
return response.data;
})
.catch(err => {
console.log(err);
});
}
const clickSubmit = (event) =>{
event.preventDefault();
signup({name, email, password})
.then(data => {
if(data.error){
setValues({...values, error: data.error, success: false})
}
else{
setValues({...values, name: '', email: '', password: '', error:'', success:true})
}
})
}
The error that is coming after writing the above code with axios library is:
Unhandled Rejection (TypeError): Cannot read property 'error' of undefined
What is wrong in the code with axios ?
Note:
Apparently I narrowed it down to the place where undefined is coming.
signup({name, email, password})
.then(data => {
if(data.error){
setValues({...values, error: data.error, success: false})
}
else{
setValues({...values, name: '', email: '', password: '', error:'', success:true})
}
})
}
Here in the .then() block 'data' is coming as undefined and I don't know why as with fetch api its working fine.
Reference the documentation of Axios found here: https://github.com/axios/axios#response-schema
What you are missing is
.then(response => {
return response.data;
})
instead of:
.then(response => {
return response;
})