How to send updated state in axios in React? - reactjs

I am trying to send post request using axios in Reactjs.
I have two component a timer component and App component and in App component i am trying to submit a form and send an axios call when i fetch the time from Timer component and save itinto counter state
I have written a condition if counter is true then update my state and then further send the post request
Working Demo
here is a handle submit code:
const handleSubmit = e => {
console.log("handleSubmit");
e.preventDefault();
if (counter) {
console.log(counter);
const url = `url string`;
setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
});
console.log(state);
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}
};
The problem is when counter is true its not update the state which causes error while send axios request.
I have consoled each and every thing but still it fails.
It seems there is lot of rendering.

If you are using class components, you can make the reuqest after the state has been set. Something like this:
this.setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
}, () => {
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
});
Since you did set the react-hooks tag, I guess that approach is not what you need. In your case, I suggest saving new state in some temporary variable and than passing that variable to axios. Like this:
const newState = {
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
};
setState(newState);
axios
.post(url, newState)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});

setState can be executed asynchronously by React, to optimize the rendering process. For cases like this one, it can also take a callback function that is guaranteed to be executed after updating the state.
For example:
this.setState({
name:'value'
},() => {
console.log(this.state.name);
});
in this case console.log will be executed after setting the name variable.
see the docs: https://reactjs.org/docs/react-component.html#setstate

Related

React - state not updating after API call

I am trying to setState after an API call, and I know this is an async task but I can't figure out how to update my state. My code looks like this:
loadUserDetails = () => {
this.setState({
isLoading: true,
status: "Fetching user details..."
}, () => {
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
this.setState({
userProfile: data
})
if (this.state.userProfile != null)
this.loadRolesData();
})
})
});
.catch(console.log);
}
The console logs are producing the correct values but when I try to update the userProfile to data it doesn't happen. Reading the docs I can see useEffect as a solution but unsure how to implement it.
Edit:
I am initiating this from componentDidMount(). I think this is the correct place but happy to be told otherwise.
I think you did the task in the wrong order.
Do fetch for the api, afterwards do setState. Here's one simple example.
fetch(...).then(res => {
this.setState({...})
})
Please don't get confused about the second parameter of setState, that is to wait till state to finish update. Normally that is designed for some special occasion, 99% of time you don't need that.
setState doesn't update the state immediately after the call, and so that's why there is a second argument (callback). It is fired only when the update is finished. You used that second argument in your first setState call actually. So you can either do the same thing in the second call:
this.setState({
isLoading: true,
status: "Fetching user details..."
}, () => {
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
this.setState({
userProfile: data
}, () => {
// this code will get fired only after the state updates
if (this.state.userProfile != null) {
this.loadRolesData();
}
})
})
.catch(console.log);
});
Or you can use react hooks which would require you to refactor your component into a function and rewrite your fetch logic like the following:
const [userProfile, setUserProfile] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
// This function will get fired every time the userProfile state updates
React.useEffect(() => {
if (userProfile != null) {
loadRolesData();
}
}, [userProfile]);
const loadUserProfile = () => {
setIsLoading(true);
setStatus("Fetching user details...");
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
setUserProfile(data);
setIsLoading(false);
})
.catch(console.log);
};

REACT JS class Lifecycles: How to put http request in ComponentdidUpdate?

I have a http request to call once the prop taken from redux store updates as shown below:
const mapStateToProps = state => {
console.log(state.queryBuild);
return {
queryBuilderObject: state.queryBuild,
}
}
export default connect(mapStateToProps, null)(SummaryView);
Here is my componentdidupdate function:
async componentDidUpdate()
{
//console.log("Component unmount detected");
//console.log(this.props.queryBuilderObject);
this.setState({state: {
...this.state,
isLoading: true,
}});
await axios.post(ApiEndPoints.getSummaryDataByQueryBuilder,this.props.queryBuilderObject,{timeout: axiosTimeOut})
.then(response => {
console.log("REsponse:");
console.log(response);
this.setState({state: {
...this.state,
isLoading: false,
}});
})
.catch(error => console.log("Error: " + error.message));
}
now here's the problem... somehow I want to only make an http request if props.queryBuilderObject changes that comes from redux store. But when I am going this way, I am entering into an infinite loop as I am setting state and hence componentdidupdate is triggered everytime.
Can someone suggest the right way to do so?
componentDidUpdate receives the previous props and state as arguments, you can check the previous props' queryBuilderObject against the current props' queryBuilderObject and if they are not equal do the POST request.
componentDidUpdate(prevProps, prevState, snapshot)
You may call setState() immediately in componentDidUpdate() but note
that it must be wrapped in a condition
If you update state from this lifecycle function without a conditional check then it will likely cause infinite render looping.
There is also no need to spread in existing state in the setState function; setState does a shallow merge of state updates.
async componentDidUpdate(prevProps) {
if (prevProps.queryBuilderObject !== this.props.queryBuilderObject) {
this.setState({ isLoading: true });
await axios
.post(
ApiEndPoints.getSummaryDataByQueryBuilder,
this.props.queryBuilderObject,
{ timeout: axiosTimeOut }
)
.then((response) => {
this.setState({ isLoading: false });
})
.catch((error) => console.log("Error: " + error.message));
}
}

React componentDidMount not setting states before page loads

Working on a MERN application, I have a componentDidMount that uses axios to retrieve from the backend some Ids and retrieve product info(prods) from the ids. However the states in my application are still empty when the page is loaded initially, instead I'll have to make a change to state before the states are set.
I believe it might have something to do with having an array mapping in the componenDidMount, I could change the backend so in node. However i would like to see if anything could be done in the frontend first.
componentDidMount() {
axios
.get("/api/featureds")
.then(response => {
this.setState({
featureIds: response.data
});
response.data.map(({ prodId, _id }) =>
axios
.get("/api/prods/" + prodId)
.then(response => {
if (response.data == null) {
} else {
this.state.featureTempList.push(response.data);
}
})
.catch(error => {
console.log(error);
})
);
this.setState({
featureProds: this.state.featureTempList
});
})
.catch(error => {
console.log(error);
});
}
Why are you trying to set state like this?
this.state.featureTempList.push(response.data)
State should be set by this.setState().
So you can try doing this:
this.setState((oldState) => ({
featureTempList: oldState.featureTempList.push(response.data)
});
Just remember to set featureTempList to state when you initialize:
state = {
featureTempList: []
}

Cannot fetch api due to array react native

I bulid an api using laravel which can run in postman (http://lkcfesnotification.000webhostapp.com/api/notifications). The problem is when i fetch using an example from this (https://www.youtube.com/watch?v=IuYo009yc8w&t=430s) where there is a array in the api then i have to setstate the array which is working well but when i try using the below code it does not render due to it is not using array in the api for example the random user api have "results" :[item], and mine one is "data":[my item]
fetchData = async () => {
const response = await fetch("https://randomuser.me/api?results=500");
const json = await response.json();
this.setState({ data: json.results });
};
if i use this will work but i want to use below code due to some homework i am doing
type Props = {};
export default class IndexScreen extends Component<Props> {
...
this.state = {
data: [],
isFetching: false,
};
_load() {
let url = "http://lkcfesnotification.000webhostapp.com/api/notifications";
this.setState({isFetching: true});
fetch(url)
.then((response) => {
if(!response.ok) {
Alert.alert('Error', response.status.toString());
throw Error('Error ' + response.status);
}
return response.json()
})
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
.catch((error) => {
console.log(error)
});
}
https://imgur.com/a/he5mNXv this is my render
the result i get the code i run is blank is loading
The fetch request is working but you are not saving the right data in the right state property.
The issues is located in the following part:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
You are assigning the response to a variable members but saving another variable data, which does not exist.
In addition, the response is an object with more information than just the data, so what you are looking for is just the data property of the response.
This should work:
.then(({ data }) => {
this.setState({data});
this.setState({isFetching: false});
})
Here we destructure the response into the variable { data }, solving your issue.
Based on the snippets you don't use the fetched data to set it to your state:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
membersis the result of your fetched json. So either rename members to data or use data: members. If the code should work like your first function it's probably data: members.result. You can also combine the two setState calls to one single call:
this.setState({
data: members.result,
isFetching: false,
});

Jest/Enzyme/Reactjs testing function used by react component

Hi I have this function (apiCall) that calls an API inside a component and uses the data to update state (to then render a chart with chartjs). I want to test specifically the process inside componentDidMount that updates state without calling the API. After lots of time spent searching for a way of mocking this I still haven't been able to figure it out. Trying to assert the changed state from a mock apiCall function.
this is the apiCall function:
const apiCall = (uri) => {
return fetch(uri)
.then( (res) => {
return res
})
.catch( (ex) => {
return 0
})
}
export default apiCall;
// and this is the componentDidMount
componentDidMount() {
apiCall(this.props.uri)
.then((result) => result.json())
.then((result) => {
this.setState({ data: result });
})
this.setState({ legend: this.props.legend })
}
One of the options is to use fetch-mock
http://www.wheresrhys.co.uk/fetch-mock/
Use proxyquire and mock promise function

Resources