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

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

Related

How to send updated state in axios in React?

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

React Native - cancel all subscriptions and asynchronous tasks in the componentWillUnmount method

Since my app is fetching images from API and rendering the result as expected. But showing this warning is incomplete to this project and given answers aren't solved out my issue.
Moreover, it couldn't be solved with AbortController to pass the signal as a parameter in fetch call and using AbortController.abort() in componentWillUnmount
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount
method.
CODE:
componentDidMount() {
this.getImage(Flikr_URL);
}
getImage(url) {
fetch(url)
.then(response => response.json())
.then(responseJson =>
this.setState({
imageData: responseJson.photos.photo,
loading: false
})
)
.catch(err => {
console.log(err);
throw error;
});
}
componentWillUnmount() {
this.getImage();
}
If you want simple solution, this will help you. May be another good solution will be there, but for now you can do like this.
Check manually component is mounted or not.
then in componentDidMount method set flag componentMounted to true.
componentDidMount() {
this.componentMounted = true;
this.getImage(Flikr_URL);
}
getImage(url) {
fetch(url)
.then(response => response.json())
.then(responseJson => {
if (this.componentMounted) { // check here component is mounted
this.setState({
imageData: responseJson.photos.photo,
loading: false
});
}
})
.catch(err => {
console.log(err);
throw error;
});
}
In componentWillUnmount method set flag to false
componentWillUnmount() {
this.componentMounted = false;
}

ComponentWillReceiveProps is not called when we navigate between stack navigator components?

export default (DrawNav = createStackNavigator(
{
Home: { screen: Home },
QuestionDetail: { screen: QuestionDetail },
QuestionAsk: { screen: QuestionAsk }
},
{
initialRouteName: "Home",
headerMode: "none"
}
));
Home component lists questions and QuestionDetail shows detail information of the questions but here is the problem that i faced, whenever you back to home from QuestionDetail or other component i want to grab the questions and here is what i did in Home component,
componentDidMount() {
this.getQuestions();
}
componentWillReceiveProps() {
this.setState({ questions: [] }, () => {
this.getQuestions();
});
}
getQuestions() {
this.setState({ isLoading: true });
axios.get(`http://${IP_ADDRESS}/api/questions`)
.then(response => {
console.log('response data: ', response.data);
this.setState({ questions: response.data, isLoading: false })
})
.catch((err) => {
this.setState({ isLoading: false });
console.log('QUESTIONS ERR: '+err);
// this.props.history.push('/');
})
}
but componentWillReceiveProps is not called when you navigate from QuestionDetail to Home?
componentWillReceiveProps is triggered only when component prop updates and not on initial render. As the documentation states,
React doesn’t call UNSAFE_componentWillReceiveProps() with initial props during mounting. It only calls this method if some of component’s props may update. Calling this.setState() generally doesn’t trigger UNSAFE_componentWillReceiveProps().
componentWillReceiveProps is deprecated, particularly because it's often misused. For asynchronous actions componentDidMount and componentDidUpdate are supposed to be used instead of componentWillMount and componentWillReceiveProps:
If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.
If same logic is applicable to both hooks, there should be a method to reuse. There's already such method, getQuestions:
componentDidMount() {
this.getQuestions();
}
componentDidUpdate() {
this.getQuestions();
}
getQuestions() {
this.setState({ isLoading: true, questions: [] });
axios.get(`http://${IP_ADDRESS}/api/questions`)
...
}

React accessing state before ComponentDidMount

When I try to access a state variable which is set in ComponentDidMount, react throws an undefined error. This is because I believe when I'm calling the fetch api and setState in ComponentDidMount, the value isn't ready yet (async stuff). Is there a proper way to either delay the render until the setState call is done or some other way to get the state updated fully before render is called?
I think the code below will give you a basic idea how fetch data and render work.
class App extends Component {
state = {
data:{},
loading:true,
error:null,
}
componentDidMount = () => {
fetch('https://example.com/api/article')
.then((response) => {
return response.json();
})
.then((json) => {
this.setState({
data:json,
loading:false,
})
.catch(error => {
this.setState({
error,
loading:false,
})
});
});
}
render() {
const {data,error,loading} = this.state;
if(loading){
return "Loading ..."
}
if(error){
return "Something went wrong."
}
return 'your actual render component or data';
}
}
export default App;

How to use setState in an asynchronous function

I am running this code:
.then((url) => {
if (url == null || undefined) {
return this.props.image;
} else {
const { image } = this.props;
//entryUpdate is an action creator in redux.
this.props.entryUpdate({ prop: 'image', value: url })
.then(() => {
this.setState({ loading: false });
});
but I get the following error:
How do I format setState() inside an asynchronous function that's called after an action creator?
Any help would be much appreciated!
In order for this to work, your action creator this.props.entryUpdate would need to return a promise for the async work it's doing. Looking at the error message, that does currently not appear to be the case.
You also need to be aware that calling setState() in the asynchronous callback can lead to errors when the component has already unmounted when the promise resolves.
Generally a better way is probably to use componentWillReceiveProps to wait for the new value to flow into the component and trigger setState then.
I placed the .then() function inside of the the if statement. But it should be like this:
.then((url) => {
if (url == null || undefined) {
return this.props.image;
} else {
const { image } = this.props;
this.props.entryUpdate({ prop: 'image', value: url })
}
})
.then(() => {
this.setState({ loading: false });
});

Resources