Method returning undefined even though fetch succeeds? - reactjs

I have two components, Client and App, and a fetch function. App is the child component of Client. I want to update Client's state using the return value from the method App calls. However, Client's state response is undefined after the fetch. I'm not sure why this code does not work.
import React, { Component } from 'react';
import './App.css';
function post(user, token, data){
console.log('fetching...')
fetch(`/project`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+ btoa(user+':'+token),
},
body: JSON.stringify(data)
}).then(r => {
if (!r.ok)
throw Error(r.status);
r.json().then(r => {return(r)});
}).catch(error => {throw Error(error)})
}
class Client extends Component {
constructor(props) {
super(props);
this.state = {
user: '',
token: '111',
project: {'project':'demo'},
response: {},
};
this.updateState = this.updateState.bind(this);
};
updateState(){
const { user, token, project } = this.state;
post(user, token, project).then(text => this.setState({ response: text
}));
}
render() {
return (
<App updateState={this.updateState}/>
)
}
}
class App extends Component {
render() {
return (
<div className="App">
<button onClick={ () => {
this.props.updateState()} }>Fetch Project</button>
</div>
);
}
}
EDIT: I changed my post() to this and it works :)
async function post(user, token, data){
console.log('fetching...')
const response = await fetch(`/project`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+ btoa(user+':'+token),
},
body: JSON.stringify(data)
}).catch(error => {throw Error(error)});
if(!response.ok)
throw Error(response.status);
const obj = await response.json();
return(obj);
}

If you are working with promises, you can do something like this.
import React, { Component } from "react";
async function post() {
// fetch //
return await fetch("https://hipsum.co/api/?type=hipster-centric");
}
class Client extends Component {
constructor(props) {
super(props);
this.state = {
response: "12"
};
this.updateState = this.updateState.bind(this);
}
async updateState(res) {
const text = await res().then(res => res.text());
this.setState({ response: text });
}
render() {
return (
<>
{this.state.response}
<App updateState={this.updateState} />
</>
);
}
}
class App extends Component {
render() {
return (
<div>
<button
onClick={() => {
this.props.updateState(post);
}}
>
Fetch
</button>
</div>
);
}
}
export default Client;
sandbox

It will be nice to know all the code for the fetch function but I think the problem is mostly here:
this.props.updateState(post())
That call is synchronous and the fetching process isn't. You need a better approach with await or promises or a callback.

Related

API POST using fetch helper function doesn't render update after response is received

Beginner at React (and JS)...
I am trying to update text on the screen after my API helper function has completed a call. Instead, it returns empty. I have verified with the console that the data is being received. I followed the ComponentDidMount method from other similar questions but am still having no success. My code also seems to make multiple API calls, even though my intent is to only make one; I have to wonder if the issue stems from that.
Helper function:
export function apiHelper(url, data = {}, method = 'POST') {
return fetch(url, { // Return promise
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(res => res.json())
.then((result) => {
console.log(result);
return result;
}, (error) => {
error = error;
})
}
React Component that renders incorrectly after data is received:
class Headache extends React.Component {
constructor(props) {
super(props);
this.state = {
apiResponse: null,
};
}
componentDidMount() {
apiHelper(URLredacted,JSONredacted)
.then(response => {
this.setState({
apiResponse: response
});
console.log("state set.",response)
});
}
render() {
const jsonResponse = JSON.stringify(this.props.apiResponse)
return (
<div>
<img className="logoheader" src = {logo}/>
<ColoredLine color="grey"/>
<p>{jsonResponse}</p>
</div>
);
}
}
export function apiHelper(url, data = {}, method = 'POST') {
return fetch(url, { // Return promise
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(res => res.json())
.catch(err=>console.log(err))
}
second page:
class Headache extends React.Component {
constructor(props) {
super(props);
this.state = {
apiResponse: null,
};
}
componentDidMount() {
apiHelper(URLredacted,JSONredacted)
.then(response => {
this.setState({
apiResponse: response
});
console.log("state set.",response)
});
}
render() {
const jsonResponse = JSON.stringify(this.props.apiResponse)
return (
<div>
<img className="logoheader" src = {logo}/>
<ColoredLine color="grey"/>
<p>{jsonResponse && ""}</p>
</div>
);
}
}
if these code dont work you will be need asyn function because you need to wait to fetch data.

Can't Render Data with API Call

I've been stuck on this for quite some time now, I can't use a get request and have to use post as that is the only way I am able to get field values back. And no matter what I do, I can't get ANY data to render, as of right now, all i see is the loading... telling me that the data is null. yet I don't know how to change this. Any help would be appreciated.
this is using Fetch to call the QuickBase RESTful API to get multiple field values to just use as data points on line charts. I know this shouldn't be this hard, yet nothing I do can render any data. Using React as well.
import React, { Component } from 'react'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends Component {
state = {
data: null,
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(res => res.json())
.then( res => {
this.setState({
data: [],
})
});
}
render() {
const { data } = this.state;
if (data === null) return 'Loading...';
return (
<div>
<h3>
{data}
</h3>
</div>
)
}
}
export default JobsTableApi;
some users have said to map through, but the problem is I don't know how with my current code. some say to use data.value yet it's an array. i've tried data[3], since there is no 'job name' field, or 'amount' field, it's all split up by number as shown above in my select body.
Thanks,
I guess the root cause is coming from using same names in the React's fetch and as a key in the QB response.
You can try to reach data by map via data["data"][item][6].value (6 is a field ID)
I have created and tested the following and it works properly.
<div id="mydiv"></div>
<script type="text/babel">
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"XXXXXXXXXXX","select":[3,6,7],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":6,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response .json())
.then(data => this.setState({ data }));
}
render() {
const { data } = this.state;
if (data === null) return 'Loading...';
return (
<ul>
{Object.keys(data["data"]).map(item =>
<li key={item}>
<a> {data["data"][item][6].value} </a>
</li>
)}
</ul>
)
}
}
ReactDOM.render(<JobsTableApi />, document.getElementById('mydiv'))
</script>

Fetch Array data from API ReactJS

i try fetch array data from a API with token, the problem is i fail to render/bind to display,every time debug will show error message like this. Please guide me, im new to react, this is my 1st app.
how to bind array to myadapater?
error
here my code:
import React, { Component } from "react";
import ReactDOM from "react-dom";
const url = " "; //api customer
const token = " "; //token
class Client extends Component {
constructor(props) {
super(props);
this.state = {
error: undefined,
isLoaded: false,
items: []
};
this.getData = this.getData.bind(this);
}
componentDidMount() {
this.getData();
}
getData() {
return fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
})
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(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 (
<div>
<p>data {items}</p>
{items.map((items) => (
<div>{items.name}</div>
))}
</div>
);
}
}
}
export default Client;
There is only one real issue.
The use of {items} in the render method. If you want to display the actual JSON try {JSON.stringify(items)} instead.
Besides that i would also not use the same name in the map. So instead of items for the map function i would use item since you are dealing with one of the items.
<div>
<p>data {JSON.stringify(items)}</p>
{items.map((item) => (
<div>{item.name}</div>
))}
</div>
Additionally, since you only use getData in the componentDidMount you do not need to bind it in the constructor (that is required if you intent to pass that function to other component/functions outside of the current one)
And you also do not need to return anything for the getData function since you handle the result in the then handlers.
So,
class Client extends Component {
constructor(props) {
super(props);
this.state = {
error: undefined,
isLoaded: false,
items: []
};
}
componentDidMount() {
this.getData();
}
getData() {
fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
})
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(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 (
<div>
<p>data {JSON.stringify(items)}</p>
{items.map((item) => (
<div>{item.name}</div>
))}
</div>
);
}
}
}

setState not working with import class component

I have made a Class Component Token & import in App.js
In App.js, there is api having headers with Authorisation Key (i.e. Token)
I have tried this code so far
import React, {Component} from 'react';
import Token from './Token';
class App extends Component {
constructor(){
super();
this.state = { sliders: '', auth_key: ''};
}
componentWillMount(){
this.setState({
auth_key: <Token />
});
}
componentDidMount = () => {
//POST json
var dataToSend = { userid: '223' };
//POST request
fetch('API/PATH', {
method: 'POST', //Request Type
body: dataToSend, //post body
headers: {
'Content-Type': 'application/json',
'Authorization': this.state.auth_key
},
})
.then(response => response.json())
.then(responseJson => {
this.setState({ sliders: responseJson });
alert(JSON.stringify(responseJson));
})
.catch(error => {
console.error(error);
});
}
render(){
return this.state.sliders;
}
}
export default App;
this.state.auth_key in Authorisation not working. Please help to sort out this.
Token Component Class from which Token get passes to each component
import {Component} from "react";
export default class Token extends Component{
constructor(){
super();
this.state = {tokenid: ''};
}
componentDidMount = () => {
let formBody = { usertoken: '223' };
fetch('API/PATH', {
method: 'POST',
body: formBody,
})
.then(response => response.json())
.then(responseJson => {
this.setState({
'tokenid' : responseJson.authtoken
})
})
.catch(error => {
console.error(error);
this.setState({
'tokenid' : ''
})
});
}
render(){
return this.state.tokenid;
}
}

Implementing nested fetch API calls with setInterval for dashboard: fetch()+React+Typesctipt

I am trying to bring up a dashboard page with a react table. I want table to be refreshed every 2 seconds.Also I have two fetch api calls , where result of the first one is passed as a parameter to the second. Also second fetch call must set the chart data every two seconds.
Also if there are multiple tables that follows the same implementation of such fetch calls, how can I do it?
What is the better way to do it?
Help would be appreciated
import * as React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
interface IState {
data: any[];
}
export default class Dashboard extends React.Component<{},IState> {
constructor(props: any) {
super(props);
this.state = {
data: [],
}
componentDidMount()
{
const url="/api/query/fetch";
var result = fetch(url, {
method: 'post',
body : JSON.stringify({"id":"abc"),
headers: {
'Content-Type': 'application/json'
},
}).then(function(response) {
return response.json();
}).then(function(data) {
return fetch('api/query/fetch' + data.id); // want this call to be under setInterval such that chart data refreshes every 2 seconds, s uch that table data refreshes every 2 seconds
})
.then(function(response) {
return response.json();
})
.catch(function(error) {
console.log('Request failed', error)
})
result.then(function(r) {
this.setState({data:r});
});
}
render(){
return(
<ReactTable
data={this.state.data}
columns={columns} //column config object
/>
)
}
}
import * as React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
interface IState {
data: any[];
id: any;
}
export default class Dashboard extends React.Component<{}, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [],
id: null
}
}
componentDidMount() {
const url = "/api/query/fetch";
fetch(
url,
{
method: 'post',
body: JSON.stringify({ "id": "abc"}),
headers: {
'Content-Type': 'application/json'
}
}
).then(function (response) {
return response.json();
}).then(function (data) {
this.setState({
id: data.id
}, () => this.fetchData())
});
}
fetchData() {
fetch('api/query/fetch' + this.state.id)
.then(function (response) {
return response.json();
})
.then(function (r) {
this.setState({ data: r });
setTimeout(() => {
this.fetchData();
}, 2000);
})
.catch(function (error) {
console.log('Request failed', error)
})
}
render(){
return (
<ReactTable
data={this.state.data}
columns={columns} //column config object
/>
)
}
}

Resources