I'm trying to figure out how to code my current API call so that I can access each field from the API call and render it, then be able to use it across multiple components. I'm using the QuickBase API call that only allows POST to pull field values. I've been out of the game for a couple of years and can't figure out how to accurately render these to be able to be used in other components by importing the api.js file. The project is a React within Electron to pull QuickBase data, and be able to create Line Charts (7 on one page) to show a job cost/hours and the jobs included departments cost/hours. All of my data is in quickbase, I just can't figure out how to get it over to react and able to actually use it!
Here is my API call:
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
}
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 => {
if (res.ok) {
return res.json().then(res => console.log(res));
}
return res.json().then(resBody => Promise.reject({status: res.status, ...resBody}));
})
.catch(err => console.log(err))
Any help would be greatly appreciated as I've been struggling on this for awhile! Right now I'm able to get all the correct data in the Console. But don't know how to go about rendering it on my application for actual use.
Thanks!
I think you should put your code inside a function and call that function from the component where you need the data, something like
import React, { Component } from 'react'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class App 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(response => {
if (response.ok) {
return response.json().then(res => {
this.setState({
data: res,
})
});
}
return response.json().then(resBody => Promise.reject({status: response.status, ...resBody}));
}).catch(err => console.log(err))
}
render() {
const { data } = this.state;
if (data === null) return 'Loading...';
return (
<div>
{/* Do something with data */}
</div>
);
}
}
export default App;
Check the Docs, you can send the JSON in the props of the component to render it.
You can modify your code following this example.
sandbox
import { useEffect, useState } from "react";
async function apiCall() {
return await new Promise((resolve, reject) => {
// Api Call
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => resolve(json));
});
}
const TestApp = () => {
let [data, setData] = useState({ Text: "Before api call." });
useEffect(() => {
(async () => {
let res = await apiCall();
res.Text = "After api call.";
setData(res);
})();
}, []);
return (
<div>
UserId: {data.userId} id: {data.id} title: {data.title}{" "}
completed: {data.completed}
</div>
);
};
module.exports = TestApp;
Related
Why does the POST stop working when trying to set State using useEffect and fetch?
In this first example, I can see in the POST log in the Chrome console and print in Flask:
React:
import React, { useEffect, useState } from "react";
import Button from '#mui/material/Button';
import ArrowUpwardIcon from '#mui/icons-material/ArrowUpward';
import TextField from '#mui/material/TextField';
export default function Heater(props) {
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
function incrementSetpoint() {
setStatus(previousValue => {
return {
...previousValue,
"setpoint": status.setpoint + 1
};
});
};
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((error) => (console.error(error)))
});
return (
<div>
<h1>{ props.name }</h1>
<div className="inputs-container" style={{ marginBottom: '2ch' }}>
<TextField label="Setpoint" value={ status.setpoint } />
<Button variant="contained" onClick={ incrementSetpoint }>
<ArrowUpwardIcon></ArrowUpwardIcon>
</Button>
</div>
</div>
);
}
Flask:
from flask import jsonify
#app.route('/heaters', methods=['GET', 'POST'])
def heaters():
"""Control heaters and return the current heater configuration."""
if request.method == 'POST':
print(request.json)
return jsonify(request.json)
Now, if I comment out the console.log and use setState instead, it causes and infinite loop in Flask.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
});
...
After reading some other posts, I decided to add an empty array to useEffect. This stops the infinite loop. I can see that setStatus is working by adding console.log(status). However, the POST is no longer working.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
}, []); // Empty array added here
console.log(status); // New line here to confirm setStatus works
...
On the initial startup, I can see the POST log in the Chrome console and print in Flask. After it has loaded, the POST does not work. Any ideas on what is causing the issue?
When you use useEffect() bound to an empty array, it only executes once on the first run.
useEffect(() => {
// fetch code here
}, []);
I'm guessing that you want to run it again on every status update?
If that's the case, bind useEffect() to your status state.
useEffect(() => {
// fetch code here
}, [status]);
Thing is, if you setStatus in that same useEffect() hook, you end up in the infinite loop - because it runs again after the status is updated.. and then again.. and again.
So you want to create a different state heaters [heaters, setHeaters] then setHeaters after you fetch, rather than setStatus again.
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
const [heaters, setHeaters] = useState({});
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => setHeaters(response))
.catch((error) => (console.error(error)))
}, [status]);
That way, whenever you incrementSetpoint() the status will update, the fetch will run, and heaters is set.
Finally, you see your fetched data by using another useEffect() bound to the heaters state.
useEffect(() => {
console.log(heaters);
}, [heaters]);
This should be the convention.
I am trying to load items to my next.js page and it will fail:
import {getadminInfo} from '../../dataFetch/adminInfo'
import {addItem} from '../../dataFetch/catalog'
import {useState} from "react"
import { getList } from '../../dataFetch/catalogList'
export async function getStaticProps() {
const adminData = await getadminInfo()
const catlist = await getList()
return {
props: {
catlist,
adminData
}
}
}
export default function Main({allPostsData, adminData, catlist}) {
}
My function is :
export function getList() {
const pageInfo = {
page_size : "10",
page:"1"
}
const url = "http://localhost:8000/api/catalog/list?page_size="+pageInfo.page_size+"&page="+pageInfo.page;
try {
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
fData=JSON.parse(JSON.stringify(data.response))
console.log("Returned catalog")
return fData
})
.catch(error => console.log(error))
} catch (err) {
console.log(err)
}
}
The API works and I get the right info back but I cannot load it to the page:
Error: Error serializing .catlist returned from getStaticProps in "/admin/main".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I found the issue. I did not implement the fetch correctly. It should have been async.
The reason I did not get the info is because nothing was returned.
I have a REACT component:
import React from 'react';
import Weather from '../service/weatherAPI.js';
export default class DisplayWeather extends React.Component {
constructor(props){
super(props);
this.state = {
weatherData: []
}
this.getWeatherData = this.getWeatherData.bind(this);
}
componentDidMount(){
this.getWeatherData();
}
async getWeatherData(){
let data = await Weather(this.props.location)
this.setState({ weatherData: data});
console.log(this.state.weatherData)
}
This function references a function exported from another file which is using fetch to call an endpoint. All the data returns correctly from the endpoint I am calling. However, when trying to set this data onto the state, my data is undefined.
Below is my API call, just in case I have missed anything here:
const Weather = (location) => {
fetch(url, {
Method: 'GET',
headers : {
'Accept': 'application/json'
},
})
.then((raw) => raw.json())
.then((response) => {
return response
})
}
export default Weather;
Thanks in advance.
You need to return the promise like this in your weather function:
const Weather = (location) => {
return fetch(url, {
Method: 'GET',
headers : {
'Accept': 'application/json'
},
})
.then((raw) => raw.json())
.then((response) => {
return response
})
}
That way the await is working on the promise instead of just the function.
New to React, I'm currently trying to create a data table with data from an API.
I want to have a first fetch, and then run another with response from the first (id) in order to complete my table.
Here is my code :
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {},
data: []
};
}
componentDidMount() {
this.setState({
user: JSON.parse(localStorage.getItem('user'))
}, function () {
this.loadAllObjectsInfo()
});
}
// Fetch all object info in order to fill the table
loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.userId,
'bbtoken': this.state.user.secret
},
};
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((data) => {
this.setState({ data: data })
})
}
With this code, I have the data I want to render my table but I need to run another fetch to get other info with the id coming from the first request.
How can I do that nested fetch request ?
Thanks a lot,
Matthieu
You can easily manage this with async/await:
async loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.user
'bbtoken': this.state.user.secret
},
};
let response = await fetch('https://xxxxx/api/objects', requestOptions);
let data = await response.json();
// here is another fetch - change to fit your request parameters (this is just example)
let info = await fetch('https://xxxxx/api/objects/' + data.id);
this.setState({ data });
}
You can read more about async function.
#JourdanM, you should return a new fetch request from one of the then handlers. I've made a simple snippet for you. There are no data validators and spinners. This is a simple showcase. =)
A fetch request returns a promise, and you can chain promises by simply returning them from the then handlers. Here is a good article about it, it has great examples: https://javascript.info/promise-chaining
function fetchUser (user) {
return fetch(`https://api.github.com/users/${user.login}`)
}
class User extends React.Component {
state = {
user: null
}
componentDidMount () {
fetch("https://api.github.com/users")
.then(response => response.json())
.then(users => fetchUser(users[0]))
.then(response => response.json())
.then(user => {
this.setState({user})
})
}
render () {
return (
<div>
<pre>{JSON.stringify(this.state.user, null, 2)}</pre>
</div>
)
}
}
ReactDOM.render(<User />, document.querySelector("#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>
You can write the code as below.
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res1) => {
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res2) => {
this.setState({ data: res2 });
});
});
Hope this will work for you!
You can also use axios like below
axios.post(url, data, header).then(res => {
if(res.status === 200){
console.log('1st data')
axios.post(url, data, header)
.then(response => {
if (response.status === 200) {
console.log('2nd data')
} else {
console.log('2nd error')
}
});
}else{
console.log('1st error')
}
});
I have a React-Redux thunk action that retrieves categories from an API server then adds them to the Redux store:
(categoryActions.js)
export const fetchCategories = () => dispatch => (
CategoryAPI.getCategories().then(categories => {
for(const category of categories) {
const {name, path} = category
dispatch(addNewCategory(name,path))
}
})
)
It works fine when using it with the following API call:
(categoryApi.js)
const apiServerURL = "http://localhost:3001"
const headers = {
'Content-Type': 'application/json',
'Authorization': 'whatever-you-want'
}
export const getCategories = () => (
fetch(`${apiServerURL}/categories`, { headers })
.then(res => res.json())
.then(data => data.categories)
)
However, when I try to define the API constants in a different file like so:
(apiConstants.js)
export const HEADERS = {
'Content-Type': 'application/json',
'Authorization': 'whatever-you-want'
}
export const SERVER_URL = "http://localhost:3001"
and then use them in categoryApi.js:
import {
HEADERS,
SERVER_URL
} from './apiConstants'
export const getCategories = () => (
fetch(`${SERVER_URL}/categories`, { HEADERS })
.then(res => res.json())
.then(data => data.categories)
)
I get the following error from line 3 of the thunk action in categoryActions.js above:
Unhandled Rejection (TypeError): Cannot read property
'Symbol(Symbol.iterator)' of undefined
What's the problem?
The problem is your variable is capitalized, so you need to set the property correctly, because fetch expects it lowercase:
export const getCategories = () => (
fetch(`${SERVER_URL}/categories`, { headers: HEADERS })
.then(res => res.json())
.then(data => data.categories)
)
--
{ headers }
is equivalent to:
{ headers: headers }
So in your second example you have it capitalized:
{ HEADERS: HEADERS }
This is known as property shorthand