Problem with nested fetch request in React - javascript

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')
}
});

Related

How to Render Data from a POST API call in React

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;

How can I use a state in a url to make a get using axios in reactjs

I am using axios in react to get information from a django backend, I am getting the user data and I am storing it in a state in the component but I want to use one of the attributes in the user_data state in the url of another get to get more information from the backend, I do not know if I explained it correctly but here is the code :
state = {
user_data: [],
classes: []
}
componentDidMount() {
const config = {
headers: {
'Content-Type': 'application/json',
'Authorization': `JWT ${localStorage.getItem('access')}`,
'Accept': 'application/json'
}
};
axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config)
.then(
res => {
this.setState({
user_data: res.data
});
}
)
const myString = this.state.user_data.SectionNumber
axios.get(`${process.env.REACT_APP_API_URL}/Elearning/Classes/${myString}`, config)
.then(
res => {
this.setState({
classes: res.data
});
console.log(res.data);
}
)
}
I do not know how to change the state object into something that axios can understand and use in the url
At this point when you are fetching the user related data you do not need to depend on the state. You can pass the second call as a callback to the first setState so that it can update it when the promise resolves and state has been updated.
axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config)
.then(
res => {
this.setState({
user_data: res.data
});
}, () => {
const myString = this.state.user_data.SectionNumber
axios.get(`${process.env.REACT_APP_API_URL}/Elearning/Classes/${myString}`, config)
.then(
res => {
this.setState({
classes: res.data
});
console.log(res.data);
}
)
}
)
You don't need to set the state and then take from the state to use this parameter in your url. You can use promises and pseudo-synchronous code async/await and it should help.
async componentDidMount() {
const config = {
headers: {
'Content-Type': 'application/json',
'Authorization': `JWT ${localStorage.getItem('access')}`,
'Accept': 'application/json'
}
};
const userDataResponse = await axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config)
const myString = userDataResponse.data.SectionNumber;
const classesResponse = await axios.get(`${process.env.REACT_APP_API_URL}/Elearning/Classes/${myString}`, config)
this.setState({
user_data: userDataResponse.data,
classes: classesResponse.data
});
}
This is the code that worked with me
axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config)
.then(
res => {
this.setState({
user_data: res.data
});
const SectionNumber = this.state.user_data.SectionNumber
axios.get(`${process.env.REACT_APP_API_URL}/Elearning/Classes/${SectionNumber}`, config)
.then(
res => {
this.setState({
classes: res.data
});
}
)
}
)
I am also facing a similar problem and i have done exactly as you have shown but I do not see the results.
axiosInstance.get('/user/profile/' + this.state.adminCard).then(response => {
axiosInstance.defaults.headers['Authorization'] = "JWT " + response.data.access;
this.setState({
fullName: response.data.lastName + ", " + response.data.firstName,
diploma: response.data.diploma,
projectSlug: response.data.project
})
}, () => {
const slug = this.state.projectSlug;
axiosInstance.get('/user/project/' + slug).then(response => {
axiosInstance.defaults.headers['Authorization'] = "JWT " + response.data.access;
this.setState({
assignedProjectName: response.data.projectName,
assignedProjectDesc: response.data.projectDesc,
assignedProjectSupervisor: response.data.projectSupervisor
})
console.log(this.state.assignedProjectName)
})
})
On the line where I console.log(this.state.assignedProjectName), I do not even get a return, please advice.

I don't know how to get data from fetch correctly

I have a Users class where I want to get data from the server for later writing it to state and passing data from state to the child component
export default class Users extends Component {
constructor(props) {
super(props);
this.state = {
users: this.getUsers(),
};
}
getUsers = async () => {
await return fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(res => {
if(res.ok) {
res.json();
}
})
}
}
this is what the console shows me when I output data about this.state.users
I tried to look for similar situations, but I didn't find anything worthwhile, so I ask for help here. I would be grateful for any advice or help. I'm only learning asynchrony in js
if you use async await, you don't have to pass callback function, just await the promises and update the state incase of successful response.
getUsers = async () => {
try {
const response = await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
});
const users = await response.json();
this.setState({ users });
} catch (error) {
console.log(error);
}
}
and instead of calling getUsers function from the constructor, use componentDidMount
componentDidMount() {
this.getUsers();
}
and your state should be initially null or an empty array
this.state = {
users: []
};
Add componentDidMount and call getUsers and set state.
this.state = {
users: [],
};
getUsers = async () => {
return await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(response => response.json())
.then(res => { this.seState({ users: res })})
.catch(e => { console.log(e)})
}
componentDidMount = () => {
this.getUsers()
.catch(e => console.log(e)
}

Searchbar in react to call table of data

I'm trying to create a searchbar in react that creates a table of data from an API inside the current view.
async function handleSearch() {
console.log("searching...", searchRef.current?.value);
setMessage("Searching...");
var headers = {
"Content-Type": "application/x-www-form-urlencoded",
"auth-token": token,
};
fetch(
"http:"..
{
method: "GET",
headers: headers,
}
)
.then((response) => {
setMessage("");
if (response.status !== 200) {
console.log("erROR", response);
return null;
} else {
console.log("success", response);
this.searched = true;
let productList = response.json()
return productList;
}
})
.then((responseData) => {
console.log("responseData", responseData);
// setting resonseData to productList
setProductList(responseData);
});
}
For some reason this is ridiculously hard to get working.
I would imagine the code above would work and fill the table with the correct array, but is it not.
The JSON response is like this and works properly in the console with the search component currently.
Any idea on how to solve this issue?
It seems you need to add productList as state variable like below.
// We need to initialize with empty array. Otherwise while rendering component for the first time it will try to access map of undefined(productList).
const [productList,setProductList] = useState([]);
// And then in your fetch call you can store data inside productList
fetch(
"http://localhost:5000/adverts/mwap?searchTerm=" +
encodeURIComponent(searchRef.current.value),
{
method: "GET",
headers: headers,
}
)
.then((response) => {
setMessage("");
if (response.status !== 200) {
console.log("erROR", response);
return null;
} else {
console.log("success", response);
this.searched = true;
let productList = response.json()
return productList;
}
})
.then((responseData) => {
console.log("responseData", responseData);
// setting resonseData to productList
setProductList(responseData);
});

return undefined fetch inside AsyncStorage

I have a react-native app where I do a call to an api where it should return the JSON but I'm just having undefined.
export function fetchFromAPI() {
AsyncStorage.getItem('#token', (errToken, token) => {
let token = null;
const requestBody = { token: token };
return fetch(url, {
method: 'POST',
body: JSON.stringify(requestBody)
})
.then((response) => response.json())
.then((responseJSON) => {
console.log(responseJSON); // <-- this shows the correct JSON data
return responseJSON;
}).catch((error) => {
// console.error(error);
});
});
}
I also call that funcion like this:
const apiData = fetchFromAPI();
If I do console.log() inside the fetch function, it returns the JSON data but if I do to apiData, it just gets undefined.
Does anyone has some idea why its like this, I'm doing something wrong?
You can use Promise to get response from fetchFromAPI function, like
export function fetchFromAPI() {
return new Promise((resolve, reject) => {
AsyncStorage.getItem('#token', (errToken, token) => {
let token = null;
const requestBody = {
token: token
};
return fetch(url, {
method: 'POST',
body: JSON.stringify(requestBody)
})
.then((response) => response.json())
.then((responseJSON) => {
console.log(responseJSON); // <-- this shows the correct JSON data
resolve(responseJSON);
}).catch((error) => {
reject(error);
});
});
});
}
When calling the fetchFromAPI, use await, like
const apiData = await fetchFromAPI();
You can also use .then to capture the response and store it in the state, like
fetchFromAPI.then((data) => {
// use data here
});
Hope this will help!
First, you need to return the Promise created by getItem:
export function fetchFromAPI() {
return AsyncStorage.getItem('#token', (errToken, token) => {
let token = null;
const requestBody = { token: token };
return fetch(url, {
method: 'POST',
body: JSON.stringify(requestBody)
})
.then((response) => response.json())
.then((responseJSON) => {
console.log(responseJSON); // <-- this shows the correct JSON data
return Promise.resolve(responseJSON); // <-- this wraps the JSON into a Promise
}).catch((error) => {
// console.error(error);
});
});
}
Then you need to call the function like this:
fetchFromAPI().then(apiData => {...

Categories

Resources