componentDidMount not sending data after setting state - reactjs

I am decoding a token to get the current users email address and setting to facultyEmail state and sending that to the backend to get a response. But facultyEmail is empty because componentDidMount is asynchronous ,it works outside the componentDidMount() but I don't know any way to handle the axios get request with params outside the componentDidMount i dont have event to invoke it.Thanks for the help
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setState({
facultyEmail: decoded.email
});
axios
.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
.then(res => {
this.setState({
class: res.data
});
})
.catch(err => {
console.log(err);
});
console.log("courses", this.state.facultyEmail);
}

The setState is asynchronous. You have to use setState callback or async/await
using callback
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setState({
facultyEmail: decoded.email
}, () => {
axios
.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
.then(res => {
this.setState({
class: res.data
});
})
.catch(err => {
console.log(err);
});
console.log("courses", this.state.facultyEmail);
});
}
using async/await
async componentDidMount() {
try {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
await this.setState({
facultyEmail: decoded.email
});
const res = await axios.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
this.setState({
class: res.data
});
console.log("courses", this.state.facultyEmail);
} catch (err) {
console.log(err);
}
}

You are using same email you are using in setState to make the API call, there is no need for two setStates. That would cause us anomalies and is not a recommended practice. You can do this in two ways:
Way 1:
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
axios.get("faculty/Course", {
params: {
facultyEmail: decoded.email
}
}).then(res => {
this.setState({
class: res.data,
facultyEmail: decoded.email
});
}).catch(err => {
console.log(err);
});
}
render() {
console.log(this.state.class, this.state.facultyEmail);
// This will have the values from setstate triggered inside axios.
return(
<div> Sample </div>
)
}
Alternate approach:
loadDataFromApi(email) {
axios.get("faculty/Course", {
params: {
facultyEmail: email
}
}).then(res => {
this.setState({
class: res.data
});
}).catch(err => {
console.log(err);
});
}
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setStats({
facultyEmail: decoded.email
}, () => {
// The callback function would reflect the updated email.
this.loadDataFromApi(this.state.facultyEmail);
});
}

Why not just store facultyEmail in memory until the 2nd setState, avoiding the first one? The axios call is async, so you'll need to put the console.log in the render function (and you should only log it once it's actually in state).
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
const facultyEmail = decoded.email;
axios
.get("faculty/Course", { params: { facultyEmail } })
.then(res => { this.setState({ class: res.data, facultyEmail }); })
.catch(err => { console.log(err); });
}
render() {
if (this.state.facultyEmail) console.log("courses", this.state.facultyEmail);
return ();
}

Related

async function in react component isn't working when triggered from the axios request

network.services.js
axiosCall = (axiosURL) => {
// const axiosURL = "https://api.github.com/user"
axios.get(axiosURL, {
headers: {
'Authorization': `qwdvryjutmnevw`,
}
}).then((res) => {
console.log(res.data);
return res.data;
}).catch((error) => {
throw error.message;
// console.error(error);
// toast.error(error.message);
})
}
component.js
const getData = async () => {
const asyncExample = async () => {
const result = await networkServices.axiosCall("/api/v1/calendars");
const responseData = await result;
console.log(responseData);
return responseData;
}
const data = asyncExample()
data.then(function(result) {
console.log(result); // "Some User token"
})
}
Trying to get data from service to my component in const result, console form service is consoling data but component is always returning undefined instead of data from the service file. SetTimeout function is also not working in component.
You have many mistakes. I advise you to take a look at documentation about Promises
First one:
You don't return data in axiosCall
A way to return data:
axiosCall = (axiosURL) => new Promise((resolve, reject) => {
axios.get(axiosURL, {
headers: {
'Authorization': `yourTokenHere`,
}
}).then((res) => {
// return a response data
resolve(res.data);
}).catch((error) => {
// return only error message
reject(error.message);
})
})
to use axiosCall:
try {
// don't forgot to configure axios with base url
const data = await axiosCall('/api/v1/calendars');
// do something with your data
} catch (e) {
// do something with error message
console.log(e);
}
Second:
Your make mistakes when call async function
Look at this example:
const getData = () => {
networkServices
.axiosCall("/api/v1/calendars")
.then(function(result) {
// when promise resolve
console.log(result);
})
.catch(error => {
// when promise reject
console.log(error)
})
}

Axios PUT Data with Params

My backend API route is /api/updateUser/:id
How am I supposed to POST data into this API? I'm familiar with POST request for non params APIs but this one has an /:id in the route.
Can someone show me an example with this demo code
state = {
username: "random123",
password: "random123",
userid: "qwertyuiop",
};
saveDetails = async () => {
const { username, password, userid } = this.state;
let data = new FormData();
data.append('username',username);
data.append('password',password);
axios
.put(apiEndPoint+'?id='+this.state.userid, data) //this is where I need help
.then(async (response) => {
if (response.data) {
console.log("success");
} else {
console.log("issue");
}
})
.catch((err) => {
console.log("error",err);
});
};
This is the working example for Path Parameter Axios PUT request -
saveDetails = async () => {
const { username, password, userid } = this.state;
axios
.put(apiEndPoint+"updateUser/"+userid, {
username:username,
password:password,
})
.then(async (response) => {
if (response.data) {
console.log("done");
} else {
console.log("error");
}
})
.catch((err) => {
console.log("error",err);
});
};

called async function from state is not waiting (react)

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

How to return API data to a separate component - React Native

I am Fetching data from an API in my Native App and displaying it as a List.
Below is my code:
async componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
}
this.fetchNotifications();
}
}
async fetchNotifications() {
this.setState({refreshing: true});
const config = getAppConfig();
const cognitoToken = await this.getCognitoToken(config);
if (cognitoToken !== null) {
let headers = await this.getRequestHeaders(cognitoToken);
let body = this.getRequestBody(config);
let notificationUrl = config["notification-retrieve-api"];
return fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
}).then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong');
}
})
.then((notifications) => {
console.log(JSON.stringify(notifications));
this.setState({
notifications,
error: null,
refreshing: false
});
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}
}
This works fine. I can retrieve the data from the API.
Now I want to separate the API code from my screen component. I will be calling "fetchNotifications" as a function in my screen component. I am trying to do so but it's not working at all.
This is what I'm doing:
async componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
}
let response = fetchNotifications();
this.setState({
notifications: response,
error: null,
refreshing: false
})
}
}
async function fetchNotifications() { //now this function is in another component
.
.
.
.
if(cognitoToken !== null) {
let headers = await this.getRequestHeaders(cognitoToken);
let body = this.getRequestBody(config);
let notificationUrl = config["notification-retrieve-api"];
return fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
}).then((response) => {
if (response.ok) {
response.json();
} else {
throw new Error('Something went wrong');
}
})
.then((response) => {
return response;
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}
}
export default fetchNotifications;
Is this way correct? Anyone with a better solution?
My two cents, I always put async task in Promise, including API requests.
// API helper file
export const fetchNotifications = (params) => {
return new Promise(async (resolve, reject)=>{
try{
const headers = getHeaders(params)
const body = getBody(params)
const response = await fetch(notificationUrl,
{
method: 'POST',
headers: headers,
body: body
})
if (response.ok) {
const responseObj = await response.json();
resolve(responseObj)
} else {
throw new Error('Something went wrong');
}
} catch (e) {
// something went wrong
generalHandler(e) // logging etc.
reject(e) // for ui handling
}
}
}
then we can use it everywhere
import { fetchNotifications } from '.../APIHelper'
In your ui file :
componentWillMount() {
fetchNotifications(params)
.then((notifications) => {
console.log(JSON.stringify(notifications));
this.setState({
notifications,
error: null,
refreshing: false
});
}).catch((error) => {
this.setState({
notifications: [],
error,
refreshing: false
});
});
}

Fetching methods depends on each other

I have been trying to fetch data from two sources in componentDidMount and second component's url relies on the data fetched from the first component, but it looks that state is not "updated" inside ComponenDidMount. I have tried to resolve it by using fetchDuel() in the constructor with no luck. Any suggestions? Thanks in advance!
class DuelDetail extends React.Component {
state = {
duel: [],
dataset: null
};
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
});
};
fetchDataset = () => {
axios.get(`http://127.0.0.1:8000/api/dataset/${this.state.duel.dataset}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
dataset: res.data
});
});
};
componentDidMount() {
this.fetchDuel()
this.fetchDataset()
}
Just call the second function in the then() block of the first and pass the data as a param. setState is asynchronous so you can't rely on the data to be set immediately.
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
this.fetchDataset(res.data);
});
};
As the 2 actions are async you need to handle it accordingly.
Axios get returns a promise .So you can call the second action in the then block of the first action.
Also, setState is an aync action.(It gets queued up and doesn't get triggered instantly).
Use the data received from the first action, in its then block, pass it to the second action
Just call the second function in the .then() of the first function using data from the response. Example:
class DuelDetail extends React.Component {
state = {
duel: [],
dataset: null
};
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
this.fetchDataset(res.data.dataset)
// pass whatever property you get from the response here.
});
};
fetchDataset = (datasetId) => {
axios.get(`http://127.0.0.1:8000/api/dataset/${datasetId}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
dataset: res.data
});
});
};
componentDidMount() {
this.fetchDuel()
}

Resources