how to Access variable outside of function scope? - reactjs

I have array initial value is like empty, in fetch function i am updating filterData array with data but outside of fetchData scope it's not updating filterData it's still empty
// Fetching Data and updating states
let filterData = []
const fetchData = async () => {
const req = await fetch(
"https://stratic-research-institute.firebaseio.com/articles.json"
);
let loaded = [];
const res = await req.json();
const vl = Object.keys(res);
vl.map((item) => loaded.push(res[item]));
setposts({
arData: loaded.reverse(),
loading: false,
});
filterData = loaded;
};
console.log(filterData)

You are not calling the function in the code
call fetchdata and use promise to print the value of filter data
let filterData = []
const fetchData = async () => {
const req = await fetch(
"https://stratic-research-institute.firebaseio.com/articles.json"
);
let loaded = [];
const res = await req.json();
const vl = Object.keys(res);
vl.map((item) => loaded.push(res[item]));
filterData = loaded;
};
fetchData().then(()=>{
console.log(filterData)
})

The filterData is inside the scope.
The console.log is executing before the fetch() as the fetch() is an async method.

You can try something like this because you are accessing asynchronous data... Also you can't use await outside an async function.
// Fetching Data and updating states
const fetchData = async () => {
const req = await fetch(
"https://stratic-research-institute.firebaseio.com/articles.json"
);
let loaded = [];
const res = await req.json();
const vl = Object.keys(res);
vl.map((item) => loaded.push(res[item]));
// don't have the whole context so let's comment this
/* setposts({
arData: loaded.reverse(),
loading: false,
}); */
return loaded
};
// one option
const run = async() => {
const filteredData = await fetchData();
console.log(filteredData);
}
run();
// other option
fetchData().then((r)=>console.log(r))

Related

I can't do anything with an array of arrays of objects in my react app

I collect some data from various urls into an array, using a for loop:
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i++) {
const promise = getPhotosData(i);
promise.then((items) => {
data.push(items);
});
data3 = data;
}
if I log data3 to the console, I'll get an array with multiple arrays of 50 objects each, but if I log data3.length I get 0, and of course i can't perform any iterations on it like, map, flat or forEach (what i want is to flatten data3 into a single array of objects).
I tried defining data3 as a state variable and then only flatten it (or even just get its's length), after setting it's value inside a useEffect hook, with data inside dependency array (or dependency array empty), but got same results.
You need to use Promise.all in order to wait for all promises to finish, and then assign the result to your data3 variable.
Then, use flat() and you'll get the result you want.
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let promises = [];
for (let i = 1; i < 10; i++) {
promises.push(getPhotosData(i));
}
Promise.all(promises).then((values) => {
data3 = values;
console.log(data3.length, data3.flat().length);
});
The problem is that javascript does not wait for asynchronous code to complete before going further in execution.
Reading or watching videos on code execution in javascript (event loop, call stack) will help a lot.
In this case, you're not waiting for the promise to be fulfilled and then console.log - so your code is putting out a length of 0 for data3 because the promises are not resolved yet(the API calls haven't been finished and therefore data.push(items); this code hasn't run yet).
So one way to solve this is your exact same code but you need to put an await to promise.then as shown here
const getData = async () => {
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i++) {
const promise = getPhotosData(i);
await promise.then((items) => {
data.push(items);
});
data3 = data;
}
console.log("data3 - ", data3);
};
getData()
If however you're using this in react.js context and want the data to be loaded and then use it in the page
import { useEffect, useState } from "react";
import "./styles.css";
const getData = async () => {
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i++) {
const promise = getPhotosData(i);
await promise.then((items) => {
data.push(items);
});
data3 = data;
}
return data3;
};
export default function App() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const dataResult = await getData();
setData(dataResult);
};
fetchData();
}, []);
if (data.length === 0) return <div>...loading</div>;
return (
<div className="App">
<pre>{JSON.stringify(data, null, 3)}</pre>
</div>
);
}
Keep in mind to read about promises and async/await which is basically a sugar syntax of promises.
Also your code could be optimized but that's beyond the scope of this question.
Working example of your code at codesandbox

state data still keeps the previous state even after setState

when I fetch data from my api successfully, I can display it using console.log, but somehow after I use a setState, the state still keeps the previous data. so when the page loads, the console.log(data) has an array of object containing data from the api, but console.log(rute) after the setRute still returns empty array. why is that?
this is my code
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
// console.log(rute); // [] ===> why is this empty?
}
}
any help is appreciated
useState is asynchronous, so you won't see changes after setRute. You need to wait for the component re-rendered completely
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
}
console.log(rute); //data updated on rendering
}
You can check this article for a better understanding
If you still really want to see data after setRute. You can get the result within setTimeout
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
setTimeout(() => {
console.log(rute); //data updated
})
}
}

getting child categories base on parent category id using promises

Understanding: my little understanding with promise is we can use it in place of callback functions.
Scenario: i will fetch parent categories from api than pass the categories array of objects to a function, a promise function may be ?
const getData = async () => {
const res = await fetch('API/categories?parent=0');
const data = await res.json();
return Promise.all(data.map(item => anAsyncFunction(item)))
}
this function recieves all the categories and i pass it to promise function
const anAsyncFunction = async item => {
return functionWithPromise(item)
}
this returns right data all child categories in array of objects
const functionWithPromise = async data => { //a function that returns a promise
const res = await fetch('API/categories?parent='+data.id);
const datas = await res.json();
// console.log(datas);
// api call to insert all the records
return Promise.resolve(data)
}
now i want to go through all these arrays and insert into database using api call
await api.create(postData)
You can merge anAsyncFunction & functionWithPromise functions. Two functions are unnecessary.
The line return Promise.resolve(data) can be return data
getData is thenable which gives you array of promise responses. Loop thru it twice and use Promise.all
const getData = async () => {
const res = await fetch("API/categories?parent=0");
const data = await res.json();
return Promise.all(data.map((item) => anAsyncFunction(item)));
};
const anAsyncFunction = async (data) => {
const res = await fetch("API/categories?parent=" + data.id);
const datas = await res.json();
// api call to insert all the records
// return Promise.resolve(data);
return datas;
};
getData().then((res) => {
let promises = [];
res.forEach((datas) => { //this loop is for each and every response
datas.forEach((postData) => { //this loop is for
promises.push(api.create(postData));
});
});
Promise.all(promises).then((finalRes) => {
console.log("finalRes", finalRes);
});
});

ReactJs Unable to setSate in componentDidMount from async function

I'm calling an async function (getData()) in componentDidMount, and I'm trying to use this.setState with result of that function.
componentDidMount() {
let newData = getData();
newPodData.then(function (result) {
console.log('result', result)
this.setState({result})
})
}
However, I'm having issues getting my state to properly update. Some additional context - I'm trying to set my initial state with data I am receiving from a database. Is my current approach correct? What's the best way to accomplish this? Here's my async function for more context:
const getTeamData = async () => {
const getTeamMembers = async () => {
let res = await teamMemberService.getTeamMembers().then(token => { return token });
return res;
}
const getActiveTeams = async () => {
let res = await teamService.getActiveTeams().then(token => { return token });
return res;
}
const teamMemberResult = await getTeamMembers()
const activeTeamsResult = await getActiveTeams();
// get team member data and add to teamMember object
let teamMemberData = teamMemberResult.reduce((acc, curr) => {
acc.teamMembers[curr.id] = curr;
return acc;
}, {
teamMembers: {}
});
// get team ids and add to teamOrder array
let activeTeamsData = activeTeamsResult.map(team => team.id)
let key = 'teamOrder'
let obj = []
obj[key] = activeTeamsData;
const newObject = Object.assign(teamMemberData, obj)
return newObject;
}
export default getTeamData;
Changing the function inside the then handler to an arrow function should fix it. e.g:
componentDidMount() {
let newData = getData();
newPodData.then((result) => {
console.log('result', result)
this.setState({result})
})
}
But I'll like to suggest a better way to write that.
async componentDidMount() {
let result = await getData();
this.setState({result})
}

Promise not working in React useEffect in combination with Firebase

I want to get data from firebase inside a useEffect function like this:
useEffect(() => {
/** nope */
async function fetchData() {
let dataObject = {};
let dataArray = [];
setAttendees({});
// You can await here
if (newData[listRedux]) {
const request = await Object.keys(newData[listRedux] .
[1].attendees).map(
user => {
usersRef.child(user).on('value', snap => {
dataObject[snap.key] = snap.val();
setAttendees(dataObject);
console.log(dataObject);
let comp = (
<Avatar
key={snap.key}
size="small"
source={snap.val().avatar}
alt={snap.val().name}
/>
);
dataArray.push(comp);
setAttendeesComp(dataArray);
});
}
);
// Wait for all requests, and then setState
await Promise.all(request).then(() => {
console.log('done');
});
}
}
fetchData();
}, [newData, listRedux]);
Now the second console.log inside the promise all will first show then the first console.log, meaning the request was not done yet.
How can i improve my code so the request and the states are first being set and then continue with the rest?
export default function Example() {
const [data, dataSet] = useState(false)
const [attendees, setAttendees] = useState(false)
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await res.json()
console.log(response);
dataSet(response)
}
useEffect(() => {
if (!attendees) return
fetchMyAPI();
}, [attendees, newData, listRedux]);
useEffect(() => {
setAttendees({})
}, [])
More examples here:

Resources