React: Fetching data after `componentDidMount` - reactjs

I have a general question about good practice for React data fetching.
In general, all of the tutorials I have read suggest fetching data in componentDidMount. So my lifecycle looks like this
render
componentDidMount
fetch ... setState
render
Now I have buttons which, when clicked, should fetch more data. But componentDidMount will not be called again. So what is the best practice for calling fetch again after componentDidMount has already been called?

You don't have to put the fecth call in componentDidMount. You can put your fetch in a function outside of componentDidMount, and call it in componentDidMount and call it again after any button action.
componentDidMount() {
this.fetchApi();
}
fetchApi = () => {
//call fetch
}

ComponentDidMount works only one time in component lifecycle - after first render when component is mounting. As I understand, you you need fetch more data by button clicking, so you need something like this:
async loadData() {
const processedResponse = await fetchAsyncFunction()
this.setState ({yourStateParam: processedResponse })
...
render(){
return(
<button onClick={this.loadData}
)
}

Related

React, calling a function before render

I have an async function to call before render
(the async function fetches firebase RemoteConfig and I want to use the value when rendering)
Now, I have the following structure
async componentDidMount() {
await remoteConfig.fetch()
}
Problem is that the fetch call is not guaranteed to be called before render (in my test)
How do I make sure to fetch the data before render()?
if you want to execute function before render, I think there are other hooks like getDerivedStateFromProps . But calling api before render is generally not recommended. Using componenDidMount is good, you should structure you code like:
componentDidMount(async () => {const res = await fetch(//your api)})
Once you receive the data, you call this.setState to update your state and react will automatically update the UI

How to fetch only one element

I'm trying to fetch data from api with axios, the problem that i want only one element with specific id but when i console.log i get hundred of lines of the same element
constructor(props) {
super();
const id= props.match.params.id;
this.state = {
items: [],
id: id,
}
}
render() {
axios.get(`http://api.tvmaze.com/shows/${this.state.id}`)
.then(res => {
this.setState({ items: res.data });
// console.log(this.state.items);
});
A React component gets rendered if a change happens in your props or in your state. If you update your state or your props, your UI element will get updated because the function render will be called, which causes multiple re-renderings.
Because of that, you need to move your calls to your API from your render function to another function, since your render function will potentially be called quite a few times and you don't want to send the same request to the API quite a few times. When using a class component, you can add your API call to the componentDidMount lifecycle function.
Move the axios.get to componentDidMount() lifecycle function and out of the render. As it currently is written, that will get called on every render, so every state, prop, or parent component render. If you move it to inside the lifecycle method it will call the get endpoint when the component mounts, just once.

React Native - setState doesn't work in constructor

Here is my code:
I want to set functions results which I get them as promises to some state variables but as it seems after use this.setState in then() function it returns empty object!
I checked in then() block and it contains data as it's supposed to.
class ProfileScreen extends Component{
constructor(props){
super(props);
this.state = {
profile_data: {},
user_data: {}
};
this._getUserData().then(response => {
this.setState({user_data: response});
});
//Empty Results
console.log(this.state.user_data);
this._getProfileData(this.state.user_data.id).then(response => {
this.setState({profile_data: response});
})
}
}
First of all you should never set your state in constructor using setState as a call to setState leads to rerendering of the view heirarchy which is not yet built to be rerendered.
A better place for doing so is componentDidMount because thats a lifecycle callback which ensures that the DOM is already rendered once and now you can update your state to see the changes in the view.
Coming to your problem, the empty result is due to the fact that the setState is asynchronous in nature, thus the console.log would have run even before the setState has updated the state.
You should rely on the second parameter of the setState which is a callback that runs post the state update.
this.setState({
user_data: response
}, () => {
console.log(this.state.user_data);
})
Don't do this in your constructor. Fetch your user detail in componentDidMount and then set your state as per your need
In addition to what Suraj said, if _getUserData is asynchronous, perhaps doing an AJAX call, then _getProfileData must be inside the .then callback too since you depend on the user_data to fetch the profile_data.
Try this snack out: https://snack.expo.io/#transfusion/stackoverflow-53694220
It might be cleaner to just chain the promises together.

Fetch data on Async ComponentDidMount before rendering

In my componentWillMount function, I have an async function getResults that fetches data from a server. I want to pass the response from getResults to the same component on initial load, but I noticed that my component renders before waiting for the data to come back from getResults. How do I make the component wait for the response before having it render? I am using Redux to handle my state.
async componentWillMount() {
const response = await getResults(this.props.category)
}
Async/Await only makes your code look synchronous.
async componentWillMount() {
const response = await getResults(this.props.category)
this.setState({
// use response to set your state.
});
}
Set state is what causes the re-render.
You then use the state to determine if you are in a loading state. If so it may be good to show the user. You also want to handle the default data in the first render. Once you retrieve your response you set the data to the second state.
It's idiomatic React to render your component asynchronously. You'll fetch your data, and set your state accordingly... with a new state, your component will re-render. A common practice it to render the component with a spinner, and when the state is set, re-render with the actual data. At the very least, you can render null until your async request completes and sets your state.
componentWillMount() is invoked just before mounting occurs. It is
called before render(), therefore calling setState() synchronously in
this method will not trigger an extra rendering. Generally, we
recommend using the constructor() instead.
Avoid introducing any side-effects or subscriptions in this method.
For those use cases, use componentDidMount() instead.
This is the only lifecycle hook called on server rendering.
source
You can use componentDidMount() and make the rendering conditional and waiting for the result from the API, and then render it with the result.

Unable to render content from api call in componentDidMount()

This is a simplified version of a problem I've faced while working in React. When I'm making a fetch call inside componentDidMount() and updating the state with the payload as follows:
componentDidMount(){
fetch("/api/endpoint").then(data => {
return data.json();
}).then(json => {
this.setState({
data: json
});
});
}
And rendering it out in render():
render(){
return(
<p>{this.state.data.title}</p>
)
}
I get an error saying this.state.data is undefined. I got around it by wrapping it around a conditional operator as follows:
{ this.state.data !== undefined ? <p>{this.state.data.title}</p> : null }
But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?
componentDidMount() runs after render(). You need to initialize the state correctly in the constructor
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = { data: {title: "" } };
}
componentDidMount(){
fetch("/api/endpoint").then(data => {
return data.json();
}).then(json => {
this.setState({
data: json
});
});
}
render(){
return(
<p>{this.state.data.title}</p>
)
}
}
But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?
The order of componentDidMount firing before or after render doesn't really matter here. componentDidMount has async effects. Let's say it always fires before render, then what happens is this:
componentDidMount is run. It runs fetch. Running fetch doesn't make the browser wait for fetch to return and do setState. Instead fetch sends the request to the server, and returns a promise that will be fulfilled when the response from the server returns.
The code that runs when the promise is fulfilled is the function you pass as an argument to .then(). Since your setState call is inside .then(), it will only run once the response it available.
Meanwhile, your browser goes ahead and calls render. The response from the server may or may not have returned and resolved the promise (most probably it won't have returned because network is slower than the processor executing code). So render gets called with this.state.data not yet defined. So you need to consider a state where data is undefined in render.
But my question is, if componentDidMount() fires before render() then
how can this.state.data ever be undefined?
You assumption/understanding that componentDidMount lifecycle function fires before render is wrong, componentWillMount is fired before render while componentDidMount is fired after the render method. Now you might say that since componentWillMount is fired before render, I can move my fetch call in componentWillMount and thus I would not have to add a check in the render method, but that not right. Even though your request is fired before the render the response may not always be available before the render function is executed and since all of these happen asynchronously you need to add a check for the data in render method
render(){
return(
<p>{this.state.data? this.state.data.title: null}</p>
)
}
Check Use componentWillMount or componentDidMount lifecycle functions for async request in React question for more details

Resources