I created a React application for practice. It works in my browser; however when I ran "npm test" from my terminal, it giving me a warning "Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. Please check the code for the FetchOne component."
I checked the FetchOne component but I couldn't figure it out. Is there anybody help me out, thank you!
Here is my code:
import React, {Component} from 'react';
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULE_TITLE = 'N/A title';
class FetchOne extends Component{
constructor(){
super()
this.state=({
hits: [],
isLoading: false,
error: null,
})
}
componentDidMount(){
this.setState({ isLoading: true })
fetch(API)
.then(response =>{
if(response.ok){
return response.json()
}else{
throw Error('Somethig went wrong...')
}
} )
.then( data => this.setState({
hits: data.hits,
isLoading: false,
}))
.catch(error => this.setState({
error,
isLoading: false
}))
}
render(){
const { hits, isLoading, error } = this.state;
if(isLoading){
return <p> Loading... </p>
}
if(error){
return <p>{error.message}</p>
}
return(
<div>
{hits.map(data =>
<ul key={data.objectID}>
<li>Title:<a href={data.url}> {data.title || DEFAULE_TITLE}</a></li>
<li>Author: {data.author}</li>
<li>Points: {data.points}</li>
</ul>
)}
</div>
)
}
}
export default FetchOne
Click HERE for Error Massage Screenshot
I had a similar issue previously. The issue related to the fact that the component had unmounted but I was still making a setState call. This can happen when using async functions and passing a callback which includes setState to test if this is the case.
Create
componentWillUnmount() {
console.log("Fetch One Unmounted");
}
Then see if the setState is called after the message appears.
componentDidMount(){
this.setState({ isLoading: true })
fetch(API)
.then(response =>{
if(response.ok){
return response.json()
}else{
throw Error('Somethig went wrong...')
}
} )
.then( data => this.setState({
hits: data.hits,
isLoading: false,
}, () => {console.log("setState Called")}))
.catch(error => this.setState({
error,
isLoading: false
}))
}
Related
In my react app that is based on class components, My response API got from open weather fixes after several lags.
this is my state
class Weather extends Component {
constructor(props) {
super(props);
this.state = {
weatherData: undefined,
weatherDescription: undefined,
};
}
My thinking was that when my componentDidMount,
weather API getting from openWeather and set it in state
componentDidMount() {
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=someCityId&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
and I want to update data when the city is changing, in componentDidUpdate the data get again from the openWeather
componentDidUpdate() {
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=someCityId&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
}
But the problem is that when my response receives, it faces a lag that causes data jumps several times to previous data and new data until it fixes
I do not completely understand the question, but this 'lags' because the action of fetching something from an external source is async and needs time to complete.
As for the second 'part' of displaying the loading text you have to set a variable (preferably in state which indicates the loading state of this component)
eg.
constructor(props) {
super(props);
this.state = {
loading: false,
airConditionsText: null,
// Other stuff you have in state
};
}
componentDidUpdate() {
this.setState({loading: true}) // Start of loading
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=${this.state.inputId}&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
.finally(() => this.setState({loading: false})) // End of loading
.finally is being trigger once the async operation (fetching the data from weatherAPI) finishes with either error or success which is the time to stop loading.
Then you can use this.state.loading in component render to show loading text
eg.
render() {
return (
<div>
{this.state.loading
? <div> Loading... </div>
: <div>{this.state.airConditionsText}</div> // other stuff you want to display
}
</div>
);
}
I am new here and with React. I tried to get data via APı but I could not get any. Also, I did not get any error message as well. This is my code. I tried to add if statement to check whether data is fetched or not. When I check console I got the error message. Where did I wrong? Thanks for your time.
import React, {Component} from "react";
class PokemonList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
};
}
componentDidMount() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=20')
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error,isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items ? items.map(item => (
<li>
<p key={item.id}>{item.name}</p>;
</li>
)) :
console.log('no data')}
</ul>
);
}
}
}
export default PokemonList;
The Pokémon items you're trying to get have a results key, not items. So replace result.items with result.results and see what happens.
I have to fetch data from the server to render the page. In case the server is down or data fetch fails for some reason, I have no data to show. I could show the server is down error message. My data fetch call is in componentDidMount here:-
async componentDidMount() {
this.setState({ profiles: await CurdApi.getAllProfiles() });
}
But render() is called before componentDidMount. So I am thinking to call CurdApi.getAllProfiles() from componentWillUnmount or from the constructor. But componentWillUnmount is deprecated. So the option is to call from the constructor. I am new in Reactjs so I don't know is recommended to call any API which fetches data from the server in the constructor? Or is there any other way?
Always use mount for data (or componentDidUpdate [its a little harder though])
You can use constructor to setup some defaults but sue your mount for data fetching. Use try catch to capture errors. Before rendering data please check some values like is there an error? is there data in profiles? is there a loading state?
constructor(props) {
super(props);
this.state = {
profiles: [],
isLoading: true,
}
}
async componentDidMount() {
try {
let profiles = await CurdApi.getAllProfiles();
this.setState({ profiles, error: null, isLoading: false });
} catch (error) {
let error = error.message;
this.setState({ error, isLoading: false });
}
}
render(){
return (
<React.Fragment>
{this.state.isLoading && <Spinner />}
{this.state.error && <p>this.state.error</p>}
{this.state.profiles.map(profile => <Profile key={profile.key} {...profile} >)}
</React.Fragment>
)
}
Try something like this
componentDidMount() {
this.apiCall()
}
...
const apiCall = () => {
this.setState({ profiles: await CurdApi.getAllProfiles() });
}
...
render() {
return(<div>{this.state.profiles ? this.state.profiles : null}</div>)
}
I think you mean componentWillMount and not componentWillUnmount here, right? I would recommend using componentDidMount. Thats the react way, which is why useEffect doesn't even support componentWillMount (can't find the reference...).
Essentially you just have to block the page from showing while its loading your data:
...
async componentDidMount() {
this.setState({ profiles: await CurdApi.getAllProfiles() });
}
....
render(){
<>
{state.profiles && <Profiles profiles={profiles} />}
</>
}
I would recommend you using hooks tho, I find it way easier to work with hooks:
...
const [state, setState] = useState({});
const fetchProfiles = async() =>{
setState({ profiles: await CurdApi.getAllProfiles()});
}
useEffect(()=>{
fetchProfiles();
}, [])
return(
<>
{state.profiles && <Profiles profiles={profiles} />}
</>
)
...
async componentDidMount() {
await CurdApi.getAllProfiles().then(resp=>{
this.setState({ profiles: resp});
})
}
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;
In my app component I am fetching couple things so there's couple actions. It's a state component. When one of the actions ends isLoading property changes to false and screen loading disappears. But it doesn't work properly because one action can take longer than another. How Can I change my isLoading property to false after all async actions are done?
My code looks something like
componentDidMount() {
this.props.fetchA();
this.props.fetchB();
this.props.fetchC().then(() => {
this.setState({isLoading: false})
})
}
You can chain those promises like this
componentDidMount() {
this.setState({ isLoading: true}); // start your loader
this.props.fetchA()
.then(() => {
return this.props.fetchB();
})
.then(() => {
return this.props.fetchC()
})
.then(() => {
this.setState({ isLoading: false }); // Once done, set loader to false
})
.catch(error => {
console.log('Oh no, something went wrong', error);
});
}
or using async/await with try catch do something fancy like this.
constructor () {
super();
this.state = {
isLoading: false,
};
this.onLoadData = this.onLoadData.bind(this); // see what I did here, i binded it with "this"
}
componentDidMount() {
this.onLoadData(); // Call you async method here
}
async onLoadData () {
this.setState({ isLoading: true}); // start your loader
try {
const awaitA = await this.props.fetchA();
const awaitB = await this.props.fetchB();
const awaitC = await this.props.fetchC();
this.setState({ isLoading: false }); // Once done, set loader to false
} catch (e) {
console.log('Oh no, something went wrong', error);
}
}