I try to follow some tutorial on youtube, and trying to build an admin dashboard
the tutorial uses some dummy data for the portal
export const ordersData = [
{
OrderID: 10248,
CustomerName: 'Vinet',
},
{
OrderID: 345653,
CustomerName: 'Carson Darrin',
}, etc...
i try to replicate with the real API interaction
export const ordersData = fetch('API_URL', {method:"GET"})
.then(res => {
return res.json();
}).then(data => {
console.log(JSON.parse(data.body))
return JSON.parse(data.body)
})
but its seems not to work as expected, i try to compare the console.log with the directly input data
they look the same in the console, both of them are shown as objects (typeof).
not sure which step is going wrong
The problem is here you want to return from fetch browser API, which returns Promise. If you really want to do it, for instance, instead of using useState hook in React to call and save the response in component state, then you need to wrap your fetch request into async IIFE function and use await statement to wait the async request to be fulfilled and the data to be resolved by Promise. For more info, you can refer: JS Fetch API access return value
write your fetch in an useEffect and set its response in a state
const [data, setData] = useState()
useEffect(() => {
fetch('API_URL', {method:"GET"})
.then(res => {
return JSON.parse(res);
}).then(response => {
setData(response)
})
}, [])
Related
I am trying to retrieve several "game" objects from Firebase Storage, calculate some statistics on them, and then display the game statistics in a table. The outline of my code is as follows:
function calculateTeamStatistics(team) {
// Iterating over all games, looking for team name in file, crunching the statistics
}
useEffect(() => {
async function prepareStatistics() {
// Iterating over all games, identifying teams (there is no list of teams)
calculateTeamStatistics(team)
}
prepareStatistics();
}, []);
useEffect(() => {
async function getAllGames(tournament: string) {
let gameFileReferences: any = [];
let allGames: any[] = [];
const storageRef = storage.ref();
let gameRef = storageRef.child(`${tournament}_results/`).listAll();
await gameRef.then((gameFiles) => {
gameFileReferences = gameFiles.items;
gameFileReferences.forEach((game: any) => {
storageRef
.child(`${tournament}_results/${game.name}`)
.getDownloadURL()
.then((url) => {
axios
.get(url, {
headers: {
Accept: "application/json",
},
})
.then((response) => {
allGames.push(response);
if (lastModifiedText === "" && response.headers) {
setLastModifiedText(response.headers["last-modified"]);
}
})
.catch((error) => console.log(error));
});
});
});
setTournamentGames(allGames);
}
getAllGames(tournamentId);
}, []);
The setLastModifiedText is the only hook that currently works, updating the text in the render() when I refresh in the browser. None of the statistical calculations display, however, unless I change something in the render() and save the file. When I attempt to perform the calculations and iterations inside the .then() call, where the setModifiedText, I run into more issues.
I am having difficulty figuring out what the issue is. I figure one of my async or useEffect() calls is out of place, but do not know where. Thanks for any help you can provide!
I attempted to change various async and useEffect() calls, as well as refactoring my functions, but no combination solved the problem.
I don't know if this is the optimal answer but this pattern worked for me:
const [data, setData] = useState(**your default value**);
const [object, setObject] = useState(**your default value**);
useEffect(() => (setData(**your async call**), []);
useEffect(() => (setObject(data)), [data]);
The key is to make the async call for data in the background, then use another useEffect that has a dependency to the data being retrieved. When that data changes upon completion of the async call, the useEffect with a dependency on the retrieved data will run and force a rerender.
I'm trying to make the home page send one API call on load and display all results on screen. It seems to send the call and receive response fine, although despite receiving the response from the server it can't pass the contents of the payload within the code, which is a JSON.
useEffect(() => {
const localUser = localStorage.getItem("user");
if (localUser) {
const foundUser = localUser;
setUser(foundUser);
} else {
const newUser = uuidv1();
localStorage.setItem(newUser, user);
setUser(newUser);
}
console.log(user);
async function fetchPosts() {
try {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
.then(response.payload.forEach(object => tempPosts.push(object.post)))
.then(setPosts((posts) => [tempPosts]));
console.log(posts);
} catch (err) {
console.log(err)
}
}
fetchPosts();
}, [user, posts]);
Somehow React is trying to access the response without the declaration and I have no idea how, which in result stops the function from executing.
Take a look at this line:
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
You're combining two paradigms - asynchronous programming using Promises and callbacks with then, and asynchronous programming using async/await. You'll usually want to pick one or the other for use in a single function, and you definitely cannot combine them in a single line (or at least, not like this).
If you want to use async (and I would recommend this approach), you'll probably want something like this:
async function fetchPosts() {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts');
const data = await response.json();
data.payload.forEach(object => tempPosts.push(object.post))
return tempPosts;
}
I don't know what data looks like, so you may have to play with the 4th line of the function, but hopefully you get the gist. You'll probably want to define this function outside of your component, and certainly not within the useEffect hook.
An example of how you could use this to fetch posts on the first render of your component is
useEffect(() => {
fetchPosts().then(data => setPosts(data));
}, []);
assuming you have a relevant useState hook.
I have a React app built with the Minimal template and I'm trying to follow along with one of their tutorials, in order to create a Redux slice that feeds some data to a custom component. The data itself is collected from Firebase. Below is my code:
firebase.js - helper
export function getDocuments(col) {
const colRef = collection(db, col);
const q = query(colRef, where('uid', '==', auth.currentUser.uid));
getDocs(q).then((snap) => {
const data = snap.docs.map((d) => ({ id: d.id, ...d.data() }));
return data;
});
// return [1,2,3]
}
product.js - Redux slice
export function getProducts() {
return async (dispatch) => {
dispatch(slice.actions.startLoading());
try {
const products = await getDocuments('products');
dispatch(slice.actions.getProductsSuccess(products));
} catch (error) {
dispatch(slice.actions.hasError(error));
}
};
}
ProductList.js - component
const dispatch = useDispatch();
const { products } = useSelector((state) => state.client);
useEffect(() => {
dispatch(getProducts());
}, [dispatch]);
useEffect(() => {
if (products.length) {
// setTableData(products);
}
}, [products]);
If I console log data in the helper function (firebase.js), I get the values I expect, once the promise is resolved/fulfilled. However, if I console.log clients in the product.js slice or later in the component, I get undefined.
I assume my problem is not being able to understand how async + await + useEffect work together in order to fix this. My assumption is that I am trying to access the value before the promise is resolved and therefore before the helper function returns it. I confirmed that by returning a simple array [1, 2, 3] in my helper function as a test.
I think I am missing something fundamental here (I am not very experienced with React and JS in general and still learning things on the go). Can someone help me understand what am I doing wrong?
Thank you!
With await you can await the fulfillment or rejection of a promise, but your getDocuments Function does not return a promise. Change the last line of the function to the following:
return getDocs(q).then((snap) => {
const data = snap.docs.map((d) => ({ id: d.id, ...d.data() }));
return data;
});
Async and Await are no different in React than in plain JavaScript:
When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method
useEffect():
By using this Hook, you tell React that your component needs to do something after rendering. This function will run every time the component is re-rendered.
I have a simple functional component, which includes a fetch to a JSON file stored locally.
I am trying to bring in the data from the JSON file, and whilst this should be a simple task, I have come across a problem.
My console log is showing two separate logs - one is an empty object, presumably from the useState definition and the second has the data from the fetch inside it.
Therefore, When I try to do anything with the data I'm fetching, undefined is being returned, So I can't use it. I feel like I'm missing something obvious, but I'm not entirely sure what is going on here. I have tried async/await without success.
What am I missing ?
const Landing = () => {
const [api, updateApi] = useState({});
const getData = () => {
fetch('data.json')
.then((response) => response.json())
.then((data) => updateApi({api: {data}}))
}
useEffect(() => {
getData();
}, []);
console.log(api)
return (
<p>Hey!</p>
)
}
All you need to do is to wrap the return within an if/else block, and return (for example a spinning circle) loading indicator. When it re-renders with the data from the api, it returns the desired representation of the data.
Using async/await syntax you can put your fetch requests within the useEffect hook and set your state similar to how you are currently doing.
export default function Landing() {
const [api, updateApi] = useState({});
useEffect(() => {
const getData = async () => {
const response = await fetch("data.json");
const data = await response.json();
updateApi({ api: { data } });
};
getData();
}, []);
return <div>{JSON.stringify(api)}</div>;
}
If you initialise the state with useState({}) then the value won't be undefined. If however you want to check for that before doing something then an if/else statement as suggested in another answer is suitable or if it is within the component return then a common pattern is return <div>{api && JSON.stringify(api)}</div>;.
So I am not the best with React. I am building an application that gets user information from an external endpoint and stores it in local storage. I realize that my react-app is loading the html before the state for that data is updated and just doesn't display it in the front end. I would like to have the application wait until those items are in local storage before rendering. Any suggestions?
you can use async and await for that.
async componentDidMount(){
await fetch()....
/* your code goes here*/
}
Using functional components, you would typically do something like:
function App() {
const [data, setData] = useState(null)
useEffect(() => {
// Fetching or other async logic here...
fetch('/some/url/here')
.then(response => response.json())
.then(data => setData) // save the result in state
}, [])
// Not loaded yet, return nothing.
if (!data) {
return null
}
// Loaded! return your app.
return <div>Fetched data: { data.myData }</div>
}