I have a fairly simple ASP.NET site with a react front-end. It has a component MetaWeatherForecast that fetches some data from an API endpoint and displays it in a table. That works fine.
After pulling in react-pull-to-refresh into the project and attaching it to the component, the table initially loads and fetches the data on the first load, but then fails as soon as I pull the table to refresh.
Here's a trimmed version of the component in its current form:
MetaWeatherForecast.js
import React, { Component } from 'react';
import authService from './api-authorization/AuthorizeService'
import Moment from 'moment';
import ReactPullToRefresh from 'react-pull-to-refresh'
export class MetaWeatherForecast extends Component {
static displayName = MetaWeatherForecast.name;
constructor(props) {
super(props);
this.state = {
locationForecast: {}, loading: true, success: true, errorMessage: null };
}
componentDidMount() {
this.populateWeatherData();
}
static renderForecastsTable(locationForecast) {
// html markup for the table
}
static renderError(errorMessage) {
// error markup
}
handleRefresh(resolve, reject) {
let success = this.populateWeatherData();
if (success)
resolve();
else
reject();
}
async populateWeatherData() {
this.setState({ locationForecast: {}, loading: true, success: true, errorMessage: null});
const token = await authService.getAccessToken();
const response = await fetch('api/metaweatherforecast/GetFiveDayForecast/44544', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const baseResponse = await response.json();
console.log(baseResponse);
this.setState({ locationForecast: baseResponse.data, loading: false, success: baseResponse.success, errorMessage: baseResponse.errorMessage });
return baseResponse.success;
}
getContent() {
let contents;
if (this.state.loading) {
contents = <p><em>Fetching forecast...</em></p>
} else {
contents = this.state.success
? MetaWeatherForecast.renderForecastsTable(this.state.locationForecast)
: MetaWeatherForecast.renderError(this.state.errorMessage);
}
return contents;
}
render() {
return (
<ReactPullToRefresh
onRefresh={this.handleRefresh}
style={{
textAlign: 'center'
}}>
<div>
<p><em>Pull down to refresh</em></p>
<h1 id="tabelLabel" >Meta Weather forecast</h1>
{this.getContent()}
</div>
</ReactPullToRefresh>
);
}
};
The error being thrown after pulling the table is as follows and is thrown inside the handleRefresh() method:
Uncaught (in promise) TypeError: this.populateWeatherData is not a function
Any ideas or suggestions would be most welcome
In react classes, you have to bind this in the constructor
constructor(props) {
...
this.<method> = this.<method>.bind(this);
}
I like using this library.
Related
I use MUIDataTable to make my table on React JS. I should get data that I want to display on the table from API, but if data in the API is 3 then the table is looping 3 times. I want to only call data once with many lines of data as a response.
Can anyone help me?
here's my code.
App.js
import React from "react";
import ReactDOM from "react-dom";
import MUIDataTable from "mui-datatables";
import axios from "axios";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
errors: null
};
}
getData = async () => {
const option = {
url: "API/URL",
method: 'POST',
data: { data: .... }
};
axios(option)
.then(response => {
this.setState({
data: response.data.data,
isLoading: false,
});
console.log(response.data);
})
// If we catch any errors connecting, let's update accordingly
.catch(error => {
console.log(error.response);
this.setState({ error, isLoading: false })
}
);
}
componentDidMount() {
this.getData();
}
render() {
const { isLoading, data } = this.state;
const columns = ["ID", "Name", "Identity"];
return (
data.map(post => {
const { id, name, identity } = post;
const data = [
[
[id],
[name],
[identity]
]
];
const options = {
filterType: "dropdown",
responsive: "scroll"
};
return (
<MUIDataTable
title={"View Data"}
data={data}
columns={columns}
options={options}
/>
);
})
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
export default App
Where's the code I must change to for data loops only and not a table?
ComponentDidMount() would run the call every time it mounts, meaning it could re-call the API on subsequent mounts. A quick fix would be to check for state on both the data values and perhaps a waitingResponse=true flag before making the call.
I have an application which needs to fetch invoice data from Stripe API (payment processor). When the invoice data has been returned, I'm trying to update my state using this.setState({invoiceData: invoices}) where invoices is a string of HTML that I build out from the data returned from the Stripe API.
The issue is that the HTML isn't being rendered and is showing as plain text. I am pretty new to React and have only just got my head around rendering states, but now I'm pretty stuck on working this one out. What do I need to do to render the HTML? Please see my code below.
import React from 'react';
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = {
invoiceData: false
}
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch('/stripe-invoices', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
customerId: '128973982'
})
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
console.log('Error with Stripe response');
}
})
.then((stripeData) => {
var invoiceCount = stripeData['result']['data'].length;
var i;
var invoices = '';
for (i = 0; i < invoiceCount; i++) {
invoices += '<div><a href="' + stripeData['result']['data'][i]['invoice_pdf'] + '" download>' + stripeData['result']['data'][i]['number'] + '</a></div>';
}
this.setState({
invoiceData: invoices
})
})
.catch((error) => {
console.log('Error: ', error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoiceData ? this.state.invoiceData : null}
</div>
);
}
}
export default BillingInvoices;
Thank you for any insight.
I've stripped out some of your code for my example to make it easier to read:
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = { invoiceData: [] }
}
componentDidMount() {
fetch('/stripe-invoices')
.then((response) => response.ok && response.json())
// Here I'm assigning the nested array to `invoiceData` immediately
// so that you don't need to map over it later
.then((data) => this.setState({ invoiceData: data.result.data }));
}
render() {
// Here we can check if the data exists. If it doesn't
// show a loading icon (or something) until it is
if (!this.state.invoiceData) <Loader />
// ...otherwise show the data
return (
<div id="billing-invoices">
// we map over the invoice data and for each invoice
// return JSX (your div with an anchor populated with that invoice data)
{this.state.invoiceData.map((invoice) => {
return (
<div>
<a href={invoice.invoice_pdf} download>{invoice.number}</a>
</div>
)
})}
);
</div>
)
}
}
You can populate invoiceData with react components using JSX like so:
let invoices = (<div>{stripeData['result']['data'].map(data => (<div><a href={data['invoice_pdf']} download>{data['number']}</a></div>))}</div>);
this.setState({invoiceData: invoices});
You can replace the content of the second then clause with the above and leave the rest of the code unchanged.
Putting the resulted json in the component state is a good idea.
But then, you should deal with this json directly in your render method, using the power of JSX.
Check the official documentation about how to use JSX.
This is a dummy example of what your component could look like with the usage of JSX:
import React from "react";
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
}
state = {
invoices: []
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch("/stripe-invoices", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
customerId: "128973982"
})
})
.then(response => {
if (response.ok) {
this.setState(invoices: response.json());
} else {
console.log("Error with Stripe response");
}
})
.catch(error => {
console.log("Error: ", error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoices.map((invoice, index) => {
return (
<div key={index}>{invoice.name}</div>
)
})}
</div>
);
}
}
export default BillingInvoices;
I am super new to react, and I have been struggling to figure out what is causing this error in the chrome console
bundle.js:15316 Uncaught (in promise) TypeError: _this2.setState is not a function
I am trying to do a simple login with facebook to a webapp, to learn the login flows.
I have setup my login on / (also my home page route). I don't think the problem is anywhere around routing or anything. This seems to be a problem with binding in react and being new to this framework - I am having a hard time trying to figure out how to solve this.
My / or home route jsx looks like this
import React, { Component } from "react";
import { browserHistory } from 'react-router';
import FacebookLogin from 'react-facebook-login';
export default class Home extends Component {
constructor() {
super();
this.state = { isAuthenticated: false, user: null, token: ''};
this.setInputState = this.setInputState.bind(this);
}
/*logout = () => {
this.setState({isAuthenticated: false, token: '', user: null})
};*/
responseFacebook(response) {
console.log(response)
const accessTokenBlob = new Blob([JSON.stringify({input_token: response.accessToken}, null, 2)], {type : 'application/json'});
const options = {
method: 'POST',
body: accessTokenBlob,
//mode: 'cors',
cache: 'default'
};
fetch('http://localhost:8880/auth/facebook', options)
.then((r) => r.json())
.then(r => {
console.log(r)
if (r.status) {
this.setState({isAuthenticated: true, user: response.id, token: response.accessToken})
}
});
}
componentDidMount() {
browserHistory.push('/');
}
render() {
console.log(this.state)
let content = this.state.isAuthenticated ?
(
<div>
<p>Authenticated</p>
<div>
{this.state.user.name}
</div>
<div>
<button onClick={this.logout} className="button">
Log out
</button>
</div>
</div>
) : (
<div>
<FacebookLogin
appId="2128489194096154"
autoLoad={true}
fields="name,id,picture"
scope="public_profile"
callback={this.responseFacebook} />
</div>
);
return (
<div className="App">
{content}
</div>
);
}
}
The problem seems to be happening on the line containing this section of the code this.setState({isAuthenticated: true, user: response.id, token: response.accessToken})
When I setup my debug on console on the browser, I am seeing this as the replaced content from the this2 error stack link:
fetch('http://localhost:8880/auth/facebook', options).then(function (r) {
return r.json();
}).then(function (r) {
console.log(r);
if (r.status) {
_this2.setState({ isAuthenticated: true, user: response.id, token: response.accessToken });
}
});
I have been at this for almost a day now, and I am completely lost - have been reading a few articles - and have not gotten anywhere. As I keep trying to work through this, if the question is not clear - pls do let me know what more details i can add.
EDIT #1
http://localhost:8880/auth/facebook this is a backend which I have written, and this is something I control. The response log from the backend and the data received at frontend is the same. This tells me that there is no issues with cors or other integration issues.
responseFacebook function is not bound to class context. So this inside responseFacebook function does not refer to the class. You can either use arrow function like this
responseFacebook = (response) => {
Or you can explicitly bind the function in constructor like this
constructor() {
super();
this.state = { isAuthenticated: false, user: null, token: ''};
this.setInputState = this.setInputState.bind(this);
this.responseFacebook = this.responseFacebook.bind(this);
}
I'm new to React - native development. I have this json data need to show in render() using {this.state.data.min.en}
{
"status": true,
"message": "good",
"data": {
"min": {
"sin": "text",
"en": " text",
"ta": "text",
"ownere": " text"
}
}
}
The code:
import React, { Component } from "react";
import {
Platform,
StyleSheet,
Text,
View,
AppRegistry,
Alert
} from "react-native";
import { Card } from "react-native-elements";
export default class Home extends Component {
constructor() {
super();
this.state = {
data: []
};
}
handlePress = async () => {
fetch("http://xxx.xx.xx.xx/index.php/testCV/home", {
method: "POST",
headers: {
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(responseJson => {
this.setState({ data: responseJson.data });
})
.catch(error => {
console.error(error);
});
};
componentDidMount() {
this.handlePress();
}
render() {
return (
<View>
<Card>{this.state.data.min.en}</Card>
</View>
);
}
}
AppRegistry.registerComponent("Home", () => Home);
I try it using above code but when i run it i get this error. I try to find way to fix it but no luck.
It's very grateful someone can help me out with this error.
Thanks
You are defaulting your data to an empty array, so when you write this.state.data.min you will get undefined, and then trying to access en on that will give rise to your error.
You could e.g. default the data to null, and wait until your data has been loaded before rendering it.
Example
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
// ...
render() {
const { data } = this.state;
if (data === null) {
return null;
}
return (
<View>
<Card>{data.min.en}</Card>
</View>
);
}
}
Could someone provide me with a little bit of guidance on my class object and how to reference it in another in my project?
Here is my RequestAPI object - request-api.js (note: I understand that there isn't much going on in it yet, but I wanted to walk before I can run)
export class RequestApi {
constructor() {
this.apiBase = '../api';
}
fetch(url, options) {
var options = options || {};
return fetch(this.apiBase + url, options)
.then(_handleResponse, _handleNetworkError);
}
_handleResponse(response) {
if (response.ok) {
return response.json();
} else {
return response.json().then(function (error) {
throw error;
});
}
}
_handleNetworkError(error) {
throw {
msg: error.message
};
}
}
Here is the React Class component that i am trying to reference it in:
import React from 'react';
import { RequestApi } from '../../../../utils/request-api.js';
class UserLayout extends React.Component {
constructor() {
super();
this.state = {
users: [],
isLoading: true
};
this.addNewUser = this.addNewUser.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
componentDidMount() {
return RequestApi.fetch('/user')
.then(json => {
this.setState({
isLoading: false,
users: json
});
})
.catch(error => {
console.error(error.msg);
});
}
// more code here...
}
I get an error in my React Component Class object: Uncaught TypeError: _requestApi.RequestApi.fetch is not a function
Can anyone provide me with some insight/assistance?
Since fetch is not a static method, you need to create an instance of RequestApi prior to calling fetch on it:
componentDidMount() {
const api = new RequestApi();
return api.fetch('/user')
.then(json => {
this.setState({
isLoading: false,
users: json
});
})
.catch(error => {
console.error(error.msg);
});
}