Theoretically, I should be able to asynchronously fetch some data and update my component inside of componentDidMount. Here's my component:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor () {
super()
this.state = {}
}
componentDidMount () {
fetch('/api/sessions')
.then(response => response.json())
.then(data => {
this.setState({ sessions: data.body })
})
}
render () {
return (
<div>
<h1>Sessions</h1>
<ul>
{this.state.sessions && this.state.sessions.map(session => {
return <li key={`session-${session._id}`}>{session._id}</li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#root'))
The component renders, and re-renders when the data is received. But I'm getting a warning:
Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the App component.
Shouldn't I be able to assume that componentDidMount implies the component actually mounted? What am I doing wrong here?
To prevent it You can maintain a state say isMounted and update it on componentWillMount and componentWillUnmount. And whenever you are asynchronously trying to set state, first check whether the component is still mounted or not.
componentWillMount = () => {
this.setState({
isMounted: true
})
}
componentWillUnmount = () => {
this.setState({
isMounted: false
})
}
componentDidMount () {
fetch('/api/sessions')
.then(response => response.json())
.then(data => {
if (this.state.isMounted) {
this.setState({ sessions: data.body })
}
})
}
Related
I am fetching the data from json file but the issue is that when i checked network tab. It's being call continuosly what can i do to resolve the problem.
: Network Tab Image
class Banner extends Component {
constructor(props) {
super(props);
this.state = {
buttonLink:"",
bannerImg:""
};
}
render() {
const a = fetch("./logo.json").then(response => response.json())
.then((temp1)=> {this.setState({buttonLink:temp1.buttonLink,bannerImg:temp1.bannerImg});
});
return(....
<img src={this.state.bannerImg}></img>
....
)
in class component you can use some lifecycle methods like componentDidMount
so put your fetch method inside of a componentDidMount not in render method because whenever you call setState() method, it will call render method again,and inside of your render method you call api and setState again, that makes an inifinite loop, do something like this:
componentDidMount(){
const a = fetch("./logo.json").then(response => response.json())
.then((temp1)=> {this.setState({buttonLink:temp1.buttonLink,bannerImg:temp1.bannerImg});
}
You can't call fetch inside render function.
You can use functional component to easily fetch the data.
import React, { useEffect, useState } from 'react';
const Banner = () => {
const [image, setImage] = useState({ buttonLink: '', bannerImg: '' });
useEffect(() => {
fetch('./logo.json')
.then((response) => response.json())
.then((temp1) => {
setImage({ buttonLink: temp1.buttonLink, bannerImg: temp1.bannerImg });
});
}, []);
return <img src={image.bannerImg}></img>;
};
export default Banner;
I have the following route in my App component:
<Route path ='/:id' component = {VideoPage} />
When this route is visited, I want the VideoPage component to use the :id parameter to make an axios request to an API, and then I want to update VideoPage's state and render out the information that the API returns.
This is what I'm currently doing in the VideoPage component, which doesn't work:
export default class VideoPage extends Component {
constructor(props){
super()
this.state = {
id: props.match.params.id,
mainVideo: defaultVideo
};
}
componentDidUpdate(){
axios.get(getVideo(this.state.id))
.then(res => {
console.log(res)
this.setState({
mainVideo: res.data
})
})
.catch(err => console.log(err))
}
render() {
// render several components, passing them pieces of state from this component
}
The problem is that when I call this.setState from within componentDidUpdate, I get an infinite loop.
So how would you go about doing this (making an API call using params from ReactRouter and the )?
You have to check the change of the state you want to track in the componentDidUpdate, because otherwise, when you call setState in componentDidUpdate it will trigger the update and cause an infinite loop.
I assume you want to call the API everytime the id state is changed. You can use the prevState parameter of componentDidUpdate, refer to this link
You can check the state by comparing the prevState with current state like this
componentDidUpdate(prevProps, prevState) {
if (this.state.id !== prevState.id) {
axios.get(getVideo(this.state.id))
.then(res => {
console.log(res)
this.setState({
mainVideo: res.data
})
})
.catch(err => console.log(err))
}
}
But componentDidUpdate is invoked after change is occured, so it won't get invoked in initial render so you have to put your API call in componentDidMount too.
componentDidMount() {
this.fetchVideo()
}
componentDidUpdate(prevProps, prevState) {
if (this.state.id !== prevState.id) {
this.fetchVideo()
}
}
fetchVideo() {
axios.get(getVideo(this.state.id))
.then(res => {
console.log(res)
this.setState({
mainVideo: res.data
})
})
.catch(err => console.log(err))
}
This was my code
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
invites: [],
};
constructor() {
super();
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
state and setState have worked for me alright for more complex codes before. This one keeps showing the same error
This is the error:
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the App component.
Add a componentDidMount() and write your request call inside it. When the component first loads the componentDidMount function will run.
Actually you can make request in constructor (React allows it but you shouldnt) but you have to make the request only after the component has been mounted or just before it is about to be mounted.
So it is wise to make your requests in componentDidMount().
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
constructor(props) {
super(props);
this.state = {
invites: [],
};
}
componentDidMount() {
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
I move user from component1 to component2 by this.props.history.push. When I am in component2 it looks like component1 is still mounted.
I tried everything as in similar problems with unsubscribe store.
import React, {Component} from 'react';
import axios from 'axios';
import {Animate} from 'react-animate-mount';
import {translateTexts} from "../App";
import store from "../../store";
class Component1 extends Component {
constructor(props) {
super(props);
this.state = {
texts: {
computing: null
},
};
}
componentWillUnmount() {
return store.subscribe(() => {
console.log('unmount');
})
}
componentWillMount() {
store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
axios.post(`/api/`)
.then((res) => {
this.setState({show: false});
setTimeout(() => {
window.location.href = '/storage/'+res.data.id
}, 600);
})
.catch((err) => {
this.props.history.push({
pathname: '/error/'+err.response.status,
state: { code: err.response.status }});
});
});
render() {
return (
<div className="box">
Something
</div>
);
}
}
export default Component1;
import React, { Component } from 'react';
import store from "../../store";
import {translateTexts} from "../App";
class Component2 extends Component {
constructor(props) {
super(props);
this.state = {
code : null,
texts: {
msg: null
}
};
}
componentWillMount() {
this.setState( {
code: this.props.location.state.code,
});
store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
}
render() {
return (
<div>
Code: {this.state.code}
<br></br>
Message: <strong>{this.state.texts.msg}</strong>
</div>
);
}
}
export default Component2;
After componentWillUnmount I would to stop subscribing store events in component, because now when my store updates I see log unmount in my console, so Component1 is somehow mounted.
Thanks for any suggestions.
Store subscribe should return the unsubscribe function which you can call at componentWillUnmount to successfully unsubscribe from the store.
you can define and store it in state directly in componentWillMount:
this.setState({
unsubscribe: store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
});
and then call unsubscribe on componentWillUnmount
componentWillUnmount() {
this.state.unsubscribe()
}
Redux docs: https://redux.js.org/api/store#subscribe
If you're using React and Redux together, you really shouldn't be interacting with the store directly in your component. Instead, you should be using the official React-Redux library, which handles all the subscription and store update process for you. See the docs page on Why Use React Redux? for more details.
Iam trying to understand and learn how to pass around data as props to other components to use. Iam trying to build a top-level hierarchy where the API Request is made in a class at top level and then the result is passed around to child components to be used as props and then in states.
The problem is that when i pass the result i get "Object Promise" in my child component. How do I access the data sent as props to child components?
As you can see in my App.js in my render() method that i created a component of the class API and pass the result from the fetchData() method as parameter to the component.
In my API.js class i used console.log to check the result but
the result i get from the logs are:
line 5: {dataObject: Promise}
line 10: undefined
App.js:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => console.log(parsedJSON.results))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.fetchData()}/>
</div>
);
}
}
export default App;
API.js
import React from 'react';
class API extends React.Component{
constructor(props){
console.log(props);
super(props);
this.state = {
dataObj:props.dataObject
};
console.log(this.state.dataObject)
}
render() {
return(
<p>""</p>
)
}
}
export default API;
Try changing App.js to this:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => this.setState({results: parsedJSON.results}))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.state.results}/>
</div>
);
}
}
export default App;
This makes sure you fetch the data in componentDidMount and it now uses state to store the data which then will be passed into your API component.
If anyone is looking for an answer using Hooks then this might help.
App.js
import API from './API';
function App(props) {
const [result, setResult] = React.useState({});
// similar to componentDidMount
React.useEffect(() => {
this.fetchData();
}, []);
fetchData() {
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
fetch(url)
.then(response => setResult(response.json()))
.catch(error => console.log(error));
}
return (
<div className="App">
<API dataObject={result}/>
</div>
);
}
export default App;
API.js
import React from "react";
function API(props) {
const [result, setResult] = React.useState(props.dataObject);
React.useEffect(() => {
setResult(result);
}, [result]);
return <p>{result}</p>;
}
export default API;
Hope it helps! And let me know if anything is incorrect.
You should fetch data in componentDidMount and not in render. Fetching the data within render causes the API request to be repeated, every time the DOM is re-rendered by react.js.
After making the GET request to the API endpoint, first parse the data into a javascript object, then set the results to state using this.setState from within your component.
From there, you may pass the data held in state to child components as props in the render function.
For example:
const App = (props) =>
<ChildComponent />
class ChildComponent extends React.Component {
constructor(props){
super(props);
this.state = {
results: []
}
}
componentDidMount(){
fetch('/api/endpoint')
.then(res => res.json())
.then(results => this.setState({results})
}
render(){
return <GrandchildComponent {...this.state} />
}
}
const GrandchildComponent = (props) =>
<div>{props.results}</div>