ReactJS - Replacing a fetch API with axios is not working - reactjs

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;
})

Related

Axios post request is not working for a register form submission

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);
})
}

How to convert fetch to axios in raectjs?

As i just know basic of axios and not able to convert this fetch to axios method. I have the following piece of code which is working perfect i am confused do i need to use Json.stringify or any other json method as what i have read is axios do automatically converts to json
here is my code
const handleFormSubmit = event => {
event.preventDefault();
setData({
...data,
isSubmitting: true,
errorMessage: null
});
fetch("https://hookedbe.herokuapp.com/api/login", {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: data.email,
password: data.password
})
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
})
.then(resJson => {
dispatch({
type: "LOGIN",
payload: resJson
})
})
.catch(error => {
setData({
...data,
isSubmitting: false,
errorMessage: error.message || error.statusText
});
});
};
changing it to axios
const url = "https://hookedbe.herokuapp.com/api/login"
const body = {
username: data.email,
password: data.password
};
axios.post(url, body)
.then(res => {
dispatch({
type: "LOGIN",
payload: res.data
})
})
.catch(error => {
console.log(error);
})
You can use axios.post, much easier syntax and no need for any json conversion:
const payload = {
username: data.email,
password: data.password
};
axios.post("https://hookedbe.herokuapp.com/api/login", payload)
.then(res => {
dispatch({
type: "LOGIN",
payload: res.data
})
})
.catch(error => {
console.log(error);
})

I am getting a 400 status code error, from the server. The server is working fine

My client and server are setup locally right now. I am trying to register a user. Basically my server is working fine, as i checked using postman. The problem is, when I send the register request the server returns, 400 status code. This usually means there is an error in the client. I think there is an error in the headers.
import axios from 'axios'
export const register = newUser => {
return axios
.post('api/register', newUser, {
headers: { 'Content-Type': 'application/json'}
})
.then(response => {
console.log(response)
})
.catch(err => {
console.log(err)
})
}
export const login = user => {
return axios
.post(
'api/login',
{
email: user.email,
password: user.password
},
{
headers: { 'Content-Type': 'application/json' }
}
)
.then(response => {
localStorage.setItem('usertoken', response.data.token)
return response.data.token
})
.catch(err => {
console.log(err)
})
}
export const getProfile = () => {
return axios
.get('api/profile', {
headers: { Authorization: `Bearer ${localStorage.usertoken}` }
})
.then(response => {
console.log(response)
return response.data
})
.catch(err => {
console.log(err)
})
}

Convert fetch actions to axios actions

I'm trying to update my actions to axios from fetch.
For example my current login looks like this:
export const login = (email, password) => {
return (dispatch) => {
dispatch({
type: 'CLEAR_MESSAGES'
});
return fetch('/login', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email,
password: password
})
}).then((response) => {
if (response.ok) {
return response.json().then((json) => {
dispatch({
type: 'LOGIN_SUCCESS',
token: json.token,
user: json.user
});
cookie.save('token', json.token, { expires: moment().add(1, 'hour').toDate() });
browserHistory.push('/account');
});
} else {
return response.json().then((json) => {
dispatch({
type: 'LOGIN_FAILURE',
messages: Array.isArray(json) ? json : [json]
});
});
}
});
};
}
So far for the conversion to axios, I have this:
export const login = (email, password) => {
// const { email, password } = this.props;
(dispatch) => {
dispatch({
type: 'CLEAR_MESSAGES'
})
axios({
method: 'post',
url: '/login',
data: { email: email, password: password }
}).then((response) => {
dispatch({
type: 'LOGIN_SUCCESS',
token: json.token,
user: json.user
});
cookie.save('token', json.token, { expires: moment().add(1, 'hour').toDate() });
browserHistory.push('/account');
})
.catch(() => dispatch({
type: 'LOGIN_FAILURE',
messages: Array.isArray(json) ? json : [json]
})
)
}}
Its not working :( and I'm not sure what I'm doing wrong - not too familiar with axios.
I'm getting this error in Google Chrome console
I believe the error is cause you're calling a json variable that is not present in your updated code.
You need to access the data via response.data. When using fetch() it made sense to convert the response with response.json() and use a thenable to tap into that json, but with axios, you can reach into the response right away without any conversions.
export const login = (email, password) => {
return (dispatch) => {
dispatch({
type: 'CLEAR_MESSAGES'
})
axios.post("/login", {email, password}).then((response) => {
dispatch({
type: 'LOGIN_SUCCESS',
token: response.data.token,
user: response.data.user
});
cookie.save('token', json.token, { expires: moment().add(1, 'hour').toDate() });
browserHistory.push('/account');
})
.catch(() => dispatch({
type: 'LOGIN_FAILURE',
messages: Array.isArray(json) ? json : [json]
})
)

showing success and error messages in react/redux app

I'm trying to add toast notifications to my app, one plugin I've been trying to use is react-toastify.
The issue I'm having is probably more a general react/redux issue more than with a plugin such as react-toastify.
I'm using a reducer to set the redux state for errors and success messages, from what I understand with the current code, each error or success message is persistent in the store until another action is called to clear them.
The issue I can't figure out is how do I trigger a toast only once. Eg. I enter the wrong credentials, it creates an error toast, but whenever the state changes and reloads (typing anything into the email or password fields) it creates another toast.
How do I get it to only show once?
userActions.js
function handleErrors(res) {
if (res.ok) {
return res.json();
} else {
return res.json().then(err => {throw err;});
}
}
export const login = (user) => dispatch => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res =>
dispatch({
type: LOGIN,
payload: res
})
)
.catch(error =>
dispatch({
type: ERROR,
payload: error
})
)
}
userReducer.js
const initialState = {
errors: '',
success: ''
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN:
return {
...state,
errors: '',
success: action.payload.message
};
case ERROR:
return {
...state,
success: '',
errors: action.payload.message
}
default:
return state;
}
}
app.js
app.post('/login', function(req, res) {
... return res.status(500).send({ message: 'Wrong credentials' });
... return res.status(200).send({ message: 'good!' });
});
login.js
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: ""
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
render() {
const { errors, login, success } = this.props;
if (success !== '') toast.success(success, {
position: toast.POSITION.TOP_CENTER
});
if (errors !== '') toast.error(errors, {
position: toast.POSITION.TOP_CENTER
});
return (
<div>
<input type="text" id="email" placeholder="Email Address" onChange={this.handleChange} />
<input type="password" id="password" placeholder="Password" onChange={this.handleChange} />
<button onClick={() => login(JSON.stringify({email: this.state.email, password: this.state.password}))}>Log In</button>
<ToastContainer />
</div>
)
}
}
const mapStateToProps = state => ({
errors: state.store.errors,
success: state.store.success
});
export default connect(mapStateToProps, {login})(Login);
You're calling toast.success or toast.error inside render which makes a new toast pop up every time you re-render the component.
The solution is simple. Move your toast calls outside render, where they will only be called once.
One way to achieve this is to return a value from your userAction.
export const login = (user) => dispatch => {
return new Promise((resolve, reject) => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res => {
dispatch({
type: LOGIN,
payload: res
})
resolve(res)
}
)
.catch(error => {
dispatch({
type: ERROR,
payload: error
})
reject(error)
}
)
}
}
Then use that value to toast in login.js.
class Login ... {
...
loginUser = () => {
this.props.login(JSON.stringify({email: this.state.email, password: this.state.password}))
.then(res => {
toast.success(res.message, { position: toast.POSITION.TOP_CENTER })
}
).catch(error => {
toast.error(error.message, { position: toast.POSITION.TOP_CENTER })
}
)
}
...
render() {
return (
...
<button onClick={this.loginUser}>Log In</button>
...
)
}
}
There are other ways to achieve the same functionality and depending on the structure of your project, you may want to toast in a more generalized way.

Resources