When i open a particular page, fetching is processing successfully and data received, however for some kind of reason, once page is refreshed the fetch is not even pending its not even visible in inspect. If something seems not right kindly point it out because I already spent ages trying to figure out why this is happening.
state = {
metaInfoDocs: [],
docs: [],
loading: false,
};
componentDidMount() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
export default class CentralDocuments extends React.Component {
render() {
return (
<GeneralDocPresenter
type={"centralInformationDocuments"}
fetchMetaDocs={() => getMetaInfoByType("central-document")}
loadDocument={(id) => getCentralDocuments(id)}
eventManager={new EventManager()}
childComponents={{
metaDocViewer: MetaInfoDocViewer,
metaView: MetaInfoListView,
}}
/>
);
}
}
You need to use componentDidUpdate() lifecycle method as well. For example:
updateDoc = () => {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
componentDidUpdate() {
this.updateDoc()
}
componentDidMount() {
this.updateDoc()
}
You are only using componentDidMount(), you need to also put the code into componentDidUpdate().
Something like this ..
state = {
metaInfoDocs: [],
docs: [],
loading: false,
};
componentDidMount() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
componentDidUpdate() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
export default class CentralDocuments extends React.Component {
render() {
return (
<GeneralDocPresenter
type={"centralInformationDocuments"}
fetchMetaDocs={() => getMetaInfoByType("central-document")}
loadDocument={(id) => getCentralDocuments(id)}
eventManager={new EventManager()}
childComponents={{
metaDocViewer: MetaInfoDocViewer,
metaView: MetaInfoListView,
}}
/>
);
}
}
fixing the mistake
Related
Im (very) new to react having come from a Java background. I am trying to refactor some existing code to use Async and await.
The error is coming right before my render function() (highlighted with *****) and am getting a "/src/App.js: Unexpected token, expected "," error and cant for the life of me figure out what is going on. Ive tried messing around with } ) and ; and cant quite track it down. Any help is appreciated.
import React, { Component } from "react";
import { FixedSizeGrid } from "react-window";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
specialties: [],
isLoaded: false,
error: null
};
}
async componentDidMount() {
const response = await fetch (url)
.then(response => response.json())
.then(body => {
const specialties = body.data.specialties;
return specialties;
})
.then(specialties => {
return specialties.map(({ _id, name }) => {
return [_id, name];
})
.then(transformed => {
this.setState({
specialties: transformed,
isLoaded: true,
error: null
});
})
.catch(error => {
this.setState({
specialties: [],
isLoaded: true,
error: error
});
});
}
render() {***********************here
if (this.state.error) {
return <span style={{ color: "red" }}>{this.state.error.message}</span>;
}
if (!this.state.isLoaded) {
return "Loading...";
}
const ITEM_HEIGHT = 35;
return (
<FixedSizeGrid
columnWidth={300}
rowHeight={35}
itemData={this.state.specialties}
height={ITEM_HEIGHT * this.state.specialties.length}
width={600}
itemSize={() => ITEM_HEIGHT}
columnCount={2}
rowCount={this.state.specialties.length}
>
{SpecialtyYielder}
</FixedSizeGrid>
);
}
}
const SpecialtyYielder = ({ columnIndex, rowIndex, data, style }) => {
return (
<div
style={{
...style,
backgroundColor:
(rowIndex + columnIndex) % 2 ? "beige" : "antiquewhite",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{data[rowIndex][columnIndex]}
</div>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You're missing a bracket and paren:
async componentDidMount() {
const response = await fetch (url)
.then(response => response.json())
.then(body => {
const specialties = body.data.specialties;
return specialties;
})
.then(specialties => {
return specialties.map(({ _id, name }) => {
return [_id, name];
})
}) // missing closing bracket and paren
.then(transformed => {
this.setState({
specialties: transformed,
isLoaded: true,
error: null
});
})
.catch(error => {
this.setState({
specialties: [],
isLoaded: true,
error: error
});
});
}
Async/Await
Basically everywhere you used then, you can just use await instead, but in a way such that you don't need a bunch of callbacks and the logic is like synchronous code:
async componentDidMount() {
try {
const response = await fetch (url)
const body = await response.json()
const specialties = body.data.specialties;
const transformed = specialties.map(({ _id, name }) => {
return [_id, name]
})
this.setState({
specialties: transformed,
isLoaded: true,
error: null
})
}
catch(error) {
this.setState({
specialties: [],
isLoaded: true,
error: error
})
}
}
Looks like you might need a better text editor ;). It's in your componentDidMount. At the very end you're missing a ), to close off your .then block and then another curly brace to close componentDidMount
async componentDidMount() {
const response = await fetch (url)
.then(response => response.json())
.then(body => {
const specialties = body.data.specialties;
return specialties;
})
.then(specialties => {
return specialties.map(({ _id, name }) => {
return [_id, name];
})
.then(transformed => {
this.setState({
specialties: transformed,
isLoaded: true,
error: null
});
})
.catch(error => {
this.setState({
specialties: [],
isLoaded: true,
error: error
});
});
})
}
This addresses your syntax error. The way you phrased the question made it seem like you thought the "resolution" to it was to use async/await. You obviously can still do a refactor. Are you interested in still exploring async/await?
You are missing }) in componentDidMount method:
import React, { Component } from "react";
import { FixedSizeGrid } from "react-window";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
specialties: [],
isLoaded: false,
error: null
};
}
async componentDidMount() {
const response = await fetch (url)
.then(response => response.json())
.then(body => {
const specialties = body.data.specialties;
return specialties;
})
.then(specialties => {
return specialties.map(({ _id, name }) => {
return [_id, name];
})
.then(transformed => {
this.setState({
specialties: transformed,
isLoaded: true,
error: null
});
})
.catch(error => {
this.setState({
specialties: [],
isLoaded: true,
error: error
});
});
})}
render() {
const ITEM_HEIGHT = 35;
return (
<FixedSizeGrid
columnWidth={300}
rowHeight={35}
itemData={this.state.specialties}
height={ITEM_HEIGHT * this.state.specialties.length}
width={600}
itemSize={() => ITEM_HEIGHT}
columnCount={2}
rowCount={this.state.specialties.length}
>
{SpecialtyYielder}
</FixedSizeGrid>
);
}
}
const SpecialtyYielder = ({ columnIndex, rowIndex, data, style }) => {
return (
<div
style={{
...style,
backgroundColor:
(rowIndex + columnIndex) % 2 ? "beige" : "antiquewhite",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{data[rowIndex][columnIndex]}
</div>
);
};
Once I am opening my DocumentViewer fetching does infinity loop and I cannot see why this is happening, could somebody maybe see where is the problem, Its something to do with state and props but i cannot figure out why, it would be amazing to know what is the problem and how to approach it
class GeneralDocPresenter extends React.Component {
state = {
metaInfoDocs: [],
docs: [],
loading: false
};
updateDoc = () => {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
};
componentDidUpdate(prevProps, prevState, snapshot) {
this.updateDoc()
}
componentDidMount() {
this.updateDoc()
}
render() {
return <Translation>
{(t) => {
if (this.state.loading) {
return (
<div style={{display: 'flex', justifyContent: 'center'}}>
<Spin size={"medium"}/>
</div>
)
}
if (this.state.metaInfoDocs.length === 0) {
return (
<div style={{display: 'flex', justifyContent: 'center'}}>
<NoDocumentsAlert><div dangerouslySetInnerHTML={{__html: t('noDocuments')}}/></NoDocumentsAlert>
</div>
)
}
return (
<DocViewWrapper docs={this.state.docs}
metaInfoDocs={this.state.metaInfoDocs.map(doc => {
return {...doc, type: this.props.type}
})}
eventManager={this.props.eventManager}
settings={this.props.settings}
childComponents={this.props.childComponents}
/>
)
}}
</Translation>
}
loadDocuments(id) {
this.props.loadDocument(id).then(r => {
this.setState({
docs: r.data
})
});
}
}
Try replacing ComponentDidUpdate from
componentDidUpdate(prevProps, prevState, snapshot) {
this.updateDoc()
}
To
componentDidUpdate(prevProps, prevState, snapshot) {
if(this.props !== prevProps){
this.updateDoc()
}
}
You can be more specific to didUpdate what to check instead of checking complete props change.
I am attempting to pull data from Open Data to put together a quick heat map. In the process, I want to add some stats. Almost everything runs well in that I have the data and am able to render the map, but I am unsure how to deal with calculations once I get the data since it takes time for data to come in. How do I set things up so that I can run a function on a state variable if it hasn't necessarily received data yet? Currently I am getting a null as the number that is passed as props to StatCard.
Below are my attempts:
App.js
import React, { Component } from 'react';
import Leaf from './Leaf';
import Dates from './Dates';
import StatCard from './StatCard';
import classes from './app.module.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data:[],
cleanData:[],
dateInput: '2019-10-01',
loading: false,
totalInspections: null,
calculate: false
};
}
componentDidMount() {
try {
this.fetchData();
} catch (err) {
console.log(err);
this.setState({
loading: false
})
}
}
fetchData=()=>{
const requestData = async () => {
await fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
.then(res => res.json())
.then(res =>
//console.log(res)
this.setState({ data: res, loading: true})
)
}
const calculateInspections = () => {
this.setState({totalInspections: this.state.data.length})
}
//call the function
requestData();
if(this.state.data) {
calculateInspections();
}
}
handleDateInput = (e) => {
console.log(e.target.value);
this.setState({dateInput:e.target.value, loading: false}) //update state with the new date value
this.updateData();
//this.processGraph(e.target.value)
}
updateData =() => {
this.fetchData();
}
LoadingMessage=()=> {
return (
<div className={classes.splash_screen}>
<div className={classes.loader}></div>
</div>
);
}
//inspection_date >= '${this.state.dateInput}'&
// https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=inspection_date >= '2019-10-10T12:00:00'
render() {
return (
<div>
<div>{!this.state.loading ?
this.LoadingMessage() :
<div></div>}
</div>
{this.state.totalInspections && <StatCard totalInspections={this.state.totalInspections} /> }
<Dates handleDateInput={this.handleDateInput}/>
<Leaf data={this.state.data} />
</div>
);
}
}
export default App;
StatCard.js
import React from 'react';
const StatCard = ( props ) => {
return (
<div >
{ `Total Inspections: ${props.totalInspections}`}
</div>
)
};
export default StatCard;
Attempt Repair
componentDidMount() {
try {
this.fetchData();
} catch (err) {
console.log(err);
this.setState({
loading: false
})
}
}
componentDidUpdate () {
if(this.state.data) {
this.setState({totalInspections: this.state.data.length})
}
}
fetchData= async ()=>{
const requestData = () => {
fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
.then(res => res.json())
.then(res =>
//console.log(res)
this.setState({ data: res, loading: true})
)
}
//call the function
await requestData();
}
So your problem is that isLoading state needs to be set synchronously before any async calls.
So in your componentDidMount:
componentDidMount() {
try {
this.setState({ loading: true }); // YOU NEED TO SET TRUE HERE
this.fetchData();
} catch (err) {
console.log(err);
this.setState({
loading: false
})
}
}
This ensures loading as soon as you make the call.
Then your call is made and that part is asynchronous.
As soon as data comes through, the loading is done:
.then(data => {
this.setState({
data: data,
loading: false, // THIS NEEDS TO BE FALSE
totalInspections: this.state.data.length
})
})
Furthermore, your render method can have multiple return statements. Instead of having conditional JSX, return your loading layout:
render() {
if (this.state.loading) {
return <div> I am loading </div>
}
return <div> Proper Content </div>;
}
Only render <StatCard /> if you have the data you need:
{this.state.totalInspections && <StatCard totalInspections={this.state.totalInspections} /> }
First of all, I don't think you need a separate function calculateInspections(). You can put that logic in the then callback.
fetchData = () => {
fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
.then(res => res.json())
.then(data => {
this.setState({
data: data,
loading: true,
totalInspections: this.state.data.length
})
})
}
Secondly, setting this.state.totalInspections is effectively redundant, since you can simple do:
{this.state.data && <StatCard totalInspections={this.state.data.length} /> }
Lastly, avoid using componentDidUpdate() hook when you're new to react. Most of the time you end up shooting yourself in the foot.
Currently your Attempt Repair just got you into an infinite render loop. This happens because whenever you call setState(), it'll call componentDidUpdate() lifecycle hook after rendering. But within componentDidUpdate() you call again setState(), which induces a follow-up call to the same lifecycle hook, and thus the loop goes on and on.
If you must use componentDidUpdate() and call setState() inside, rule of thumbs, always put a stop-condition ahead of it. In you case, it'll be:
componentDidUpdate () {
if (this.state.data) {
if (this.state.totalInspections !== this.state.data.length) {
this.setState({ totalInspections: this.state.data.length })
}
}
}
Here is my solution.
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
dateInput: '2019-10-01',
loading: false,
error: false
};
}
async componentDidMount() {
try {
await this.fetchData(this.state.dateInput);
} catch (err) {
this.setState({ loading: false, error: true });
}
}
fetchData = (date) => new Promise(resolve => {
this.setState({ loading: true });
fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${date}'&$limit=50000`)
.then(res => res.json())
.then(res => {
this.setState({ data: res, loading: false, error: false });
resolve(res.data);
});
})
handleDateInput = e => {
this.setState({ dateInput: e.target.value }) //update state with the new date value
this.fetchData(e.target.value);
}
render() {
const { loading, data } = this.state;
return (
<div>
{loading && (
<div className={classes.splash_screen}>
<div className={classes.loader}></div>
</div>
)}
{data && <StatCard totalInspections={data.length} />}
<Dates handleDateInput={this.handleDateInput} />
<Leaf data={data} />
</div>
);
}
}
There are two ways of achieving this:
You can put calculator in componentDidUpdate() and write a condition to just calculate once
componentDidUpdate(prevProps, prevState) {
const data = this.state.data;
// this line check if we have data or we have new data,
// calculate length once
if (data.length || !isEqual(data, prevState.data)) {
calculateInspections()
}
}
// isEqual() is a lodash function to compare two object or array
You can stop your rendering until data is fetched
async componentDidMount() {
await fetchData()
}
fetchData = () => {
const requestData = async() => {
await fetch(`https://data.cityofnewyork.us/resource/p937-wjvj.json?$where=latitude > 39 AND latitude< 45 AND inspection_date >= '${this.state.dateInput}'&$limit=50000`)
.then(res => res.json())
.then(res =>
//console.log(res)
this.setState({
data: res,
loading: true,
totalInspections: res.length
})
)
}
// in above situation you just setState when you are sure
// that data has come
//call the function
requestData();
}
I have a simple modal where I writte some data and submit to database. I'm trying to implement a simple loading on the button, so the user receives feedback. Unfortunately, this isn't working.
constructor(props) {
super(props);
this.state = {
isLoading: false,
show: false,
list: []
}
}
onSubmit = async event => {
ref.onSnapshot(async doc => {
if (doc.data().status === 'accepted') {
const list = await this.get(name, age, id);
this.setState(prevState => ({
isLoading: !prevState.isLoading, // this doesn't work
list: list,
show: false // state for a modal
}, () => console.log('loading on submit', this.state.isLoading)))
}
}
}
<button
onClick={this.onSubmit}
disabled={this.state.isLoading ? true : false}>
{this.state.isLoading ? 'Loading...' : 'OK'}
</button>
Thank you! :)
You want to set isLoading to true before you get your data, and then set isLoading to false when the asynchronous request is complete.
Example
function getList() {
return new Promise(function(resolve) {
setTimeout(() => resolve([1, 2, 3]), 1000);
});
}
class App extends React.Component {
state = {
isLoading: false,
show: false,
list: []
};
onSubmit = event => {
this.setState({ isLoading: true });
getList().then(list => {
this.setState({
isLoading: false,
list,
show: false
});
});
};
render() {
return (
<button
onClick={this.onSubmit}
disabled={this.state.isLoading}
>
{this.state.isLoading ? "Loading..." : "OK"}
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
trigger loading true before await call like below code.
onSubmit = async event => {
ref.onSnapshot(async doc => {
if (doc.data().status === 'accepted') {
this.setState({
isLoading : true
})
const list = await this.get(name, age, id);
this.setState(prevState => ({
isLoading: false,
list: list,
show: false // state for a modal
}, () => console.log('loading on submit', this.state.isLoading)))
}
}
}
My skills in React Native is basic, i want to insert the params id in the url to show the posts according to the category.
export default class PostByCategory extends Component {
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.Title}`,
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
return fetch(ConfigApp.URL+'json/data_posts.php?category='`${navigation.state.params.IdCategory}`)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
});
})
.catch((error) => {
console.error(error);
});
}
You have to replace navigation.state.params.IdCategory with this.props.navigation.state.params.IdCategory.
It's not a good practice to manually concat your params to the url. I suggest you look at this question to learn how to properly construct your query string.
componentDidMount() {
return fetch(ConfigApp.URL+'json/data_posts.php?category='+this.props.navigation.state.params.IdCategory)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
});
})
.catch((error) => {
console.error(error);
});
}