here is the code I am using. it doesn't make any sense to me because the error is basically telling me I am not passing it a function when using query but I am. and I am not calling the function directly either. any help would be a appreciated
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";
//import Posts from './components/Posts'
import axios from "axios";
import { useQuery } from "react-query";
import React from "react";
async function fetchPosts() {
const data = await fetch("http://swapi.dev/api/planetsd");
return data.json();
}
function App() {
const { data, status, error } = useQuery(
"stuffthing",
async () => await fetchPosts()
);
// first argumello\'ent is a string to cache and track the query result
if (status === "error") {
console.log(`${error} new error`);
}
if (status === "loading") {
return <div>loading</div>;
}
return (
<div className="container">
<h1>Posts</h1>
{data}
</div>
);
}
export default App;
I just ran into the exact same issue and it took me wayyy too long to figure out what was going on.
If you're following the documentation of v3, useQuery would indeed be used as such:
const { data, status, error } = useQuery("posts", async () => await fetchPosts());
However, in v4 it has changed to take an array as a first parameter:
const { data, status, error } = useQuery(["posts"], async () => await fetchPosts());
Three hours of my life I'll never get back.
Related
here's the jist of where I'm stuck (or just read the title for my question).
I have a firebase.js file where I have functions to authenticate. signinGithub, signinGoogle, signinEmail and so forth. The Firebase Auth business logic is in these functions.
I am showing errors with console.log or alert from these functions. The functions are imported into a Component and I don't know how to capture the functions result into the component by somehow setting state from this out-of-component function file.
Here's a basic example:
firebase.js
...
const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider)
const user = res.user
} catch (err) {
alert(err) // ** I want to pass "err" from here to Login
// ** component by updating Logins state for a message
}
}
export {signinWithGitHub}
...
Login.jsx
import React, { useEffect, useState } from "react"
import { useAuthState } from "react-firebase-hooks/auth"
import {
auth,
signInWithGitHub
} from "../lib/firebase"
function Login() {
const [user, loading, error] = useAuthState(auth)
render(
{* Below is the method call from the imported custom firebase function *}
<button onClick={signInWithGitHub}>
Login with GitHub
</button>
)
}
...
I was thinking something like this but I can't fully resolve it in my mind:
Set state in Login.js const [message, setMessage] = useState('')
When the imported signinWithGitHub has an error message --
I'm stuck figuring out how to apply to function message to the state, any ideas?
You can create a custom function inside your Login. jsx file to call the original signInWithGitHub method with a try catch block. And more importantly, you should not use render inside a functional component. Use return to render the JSX in DOM.
firebase.js
export const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider);
const user = res.user;
} catch (err) {
throw new Error(err?.message || "Unable to sign in with GitHub");
}
};
Login.jsx
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth, signInWithGitHub } from "../lib/firebase";
function Login() {
const [user, loading, error] = useAuthState(auth);
const [errorMessage, setErrorMessage] = useState("");
const onLogin = async () => {
try {
await signInWithGitHub();
} catch (err) {
setErrorMessage(err);
}
};
return (
<>
<button onClick={onLogin}>Login with GitHub</button>
{!!errorMessage && <h5>{errorMessage}</h5>}
</>
);
}
Hello my code is as follow wnna try suspense with react 18 but not having luck to render Loading....
User.jsx
import React, { useEffect, useState } from "react";
export const User = () => {
const [user, setuser] = useState(null);
useEffect(() => {
const res = fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setuser(json));
}, []);
return <div>{JSON.stringify(user)}</div>;
};
App.js
import logo from './logo.svg';
import './App.css';
import { Suspense } from 'react';
import { User } from './components/User';
function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading.....</p>}>
<User/>
</Suspense>
</div>
);
}
export default App;
the app renders {} or null but never getting the fallback as expected
Suspense is a very smart paradygm based on fetch-while-render strategy. This means that React Components from now on are able to consume directly asynchronous data. And will render the fallback while the data promise is not resolved.
The only issue is that you can't pass directly a promise, but you need a wrapper that transforms it into a Suspense consumable entity. Basically the React Component wrapped in Suspense tags, will start to try to render continuosly and it expects for a method that throws a new promise until the original promise is not resolved, that's how it knows that it has to keep rendering the fallback. So you need to pass a resource with a very specific shape, that's why you need a wrapper.
Fetching libraries like react-queris or RTK-query will implement the wrapper themselves, so you won't have to care of that part, it's not something you will have to do manually, but still if you'd like to see what's under the hood, this would be a very basic implementation of a Suspense ready fetching library:
import React, { useEffect, useState, Suspense, useRef } from 'react';
export default function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading.....</p>}>
<User />
</Suspense>
</div>
);
}
export const User = () => {
const data = useGetData('https://jsonplaceholder.typicode.com/todos/1');
return <div>{JSON.stringify(data)}</div>;
};
This is the custom hook useGetData I made:
// VERY BASIC IMPLEMENTATION OF A FETCHING LIBRARY BASED ON SUSPENSE
// This is the official basic promiseWrapper they implement in React Suspense Demo:
function wrapPromise(promise) {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
//console.log(status);
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
},
};
}
/* Basic fetching function */
const fetcher = async (url) => {
try {
const res = await fetch(url);
const data = await res.json();
await delay(2000);
return data;
} catch (e) {
throw e;
}
};
/* Util to delay loading */
const delay = (d) => new Promise((r) => setTimeout(r, d));
/* HOOK that lets to start the fetch promise only on component mount */
/* It's based on "wrapPromise" utility, which is MANDATORY to return a Suspense consumable entity */
const useGetData = (url) => {
const [resource, setResource] = useState(null);
useEffect(() => {
const _resource = wrapPromise(fetcher(url));
setResource(_resource);
}, []);
return resource?.read();
};
Suspense
The working implementation is HERE
If you want to experiment how a Suspense-ready library works, you can check this example that makes use of the great state-manager valtio which is extremely light weight and easy to use, and suspense-ready. It let's you just pass a promise in your store, and when you try to access it in a component, the component will fallback if wrapped in <Suspense>.
Check it HERE
While you can wrap your own this it is better to use a library like Relay for fetching data or an alternative state manager such as Valtio or Proxily. With Proxily it would look like this:
import "./styles.css";
import { observable, observer, suspendable } from "proxily";
import React, { Suspense } from "react";
const fetcher = {
user: () =>
wait(2000)
.then(() => fetch("https://jsonplaceholder.typicode.com/todos/1"))
.then((response) => response.json())
};
suspendable(fetcher, (f) => f.user);
const state = observable(fetcher);
function App() {
return (
<div className="App">
<Suspense fallback={<span>Loading...</span>}>
<User />
</Suspense>
</div>
);
}
export const User = observer(() => {
return <div>{JSON.stringify(state.user())}</div>;
});
const wait = (t) => new Promise((r) => setTimeout(r, t));
export default observer(App);
You can see it in action here. This article discusses Suspense in the context of concurrent rendering and transitions, demonstrating how to make Suspense part of a transition so that when fetching new data, you can continue to display the old content rather that the fallback content until the new data is fetched.
I am trying to write a function that will handle getting data to and from a server. This function takes the url to contact and uses the token to authorize itself against the server. This function is quite long. I would therefore want every other page in my react app to call this function with the needed url and then let this function handle everything else. I therefore need each page to await this function but I get "Error: Invalid hook call" no matter what I try.
This is the function that handles post requests to the server:
import React, { useEffect, useState, createRef, lazy, useContext } from "react";
import { UserContext } from "./UserContext";
import jwt_decode from "jwt-decode";
import axios from "axios";
export async function getProtectedAsset(url) {
const { user, setUser } = useContext(UserContext);
//If we do not have a token
if (user["userID"] == -1) {
return "Error: No token";
} else {
try {
//Get user data
const token = {
accessToken: user["accessToken"],
email: user["email"],
userID: user["userID"],
};
//Check if accessToken is about to expire (60s mairgain)
if (
Date.now() >=
jwt_decode(token["accessToken"])["exp"] * 1000 - 60000
) {
//Get new token
const res = await axios
.post("http://127.0.0.1:5002/refreshtoken", {
token: user["refreshToken"],
})
.then((res) => {
setUser({
userID: user["userID"],
email: user["email"],
accessToken: res.data["accessToken"],
refreshToken: user["refreshToken"],
accountType: user["accountType"],
});
})
.catch((err) => {
console.error(err);
});
}
//Our token is fresh
else {
const res = await axios
.post(url, token)
.then((promise) => {
return promise.data;
})
.catch((err) => {
console.error(err);
});
}
} catch (error) {
console.log(error);
throw err;
}
}
}
This is the page/component that I try to call this function from:
import React, { useState, useContext, useEffect, useCallback } from "react";
import { UserContext } from "../../UserContext";
import { getProtectedAsset } from "../../getProtectedAsset";
const Settings = () => {
const { user, setUser } = useContext(UserContext);
useEffect(async () => {
try {
let data = await getProtectedAsset("http://127.0.0.1:5002/mypage");
console.log(data);
} catch (error) {
console.error(error.message);
}
}, []);
return <></>;
};
export default Settings;
This gives me the error:
Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.
I have tried everything I can imagine and read different tutorials/guides/docs but still cannot figure out the problem. Usually it is the lack of knowledge, or some thinking mistakes, but I really need help with this one!
Thank you for your help
Its because you are using useContext() hook inside getProtectedAsset() function.
Instead of using useContext inside getProtectedAsset try to pass user as parameter like url to the function.
let data = await getProtectedAsset(url, user);
I want to call axios in foreach but it gives me error "Unhandled Rejection (TypeError): undefined is not iterable (cannot read property Symbol(Symbol.iterator))"
let BooksInfo=["Atomic Habit","Feel the Fear . . . and Do It Anyway","The Four Agreements"]
export default BooksInfo;
this is my first try :
import './App.css';
import Card from "./Card"
import Books from "./Books"
import { useEffect } from 'react';
import axios from "axios"
import { promised } from 'q';
function App() {
useEffect( ()=>{
async function test(){
await Promise.all(Books.forEach( (book)=>{
console.log(book + "πΌπΌπ§")
const test1= axios.get(`https://openlibrary.org/search.json?q=${book}`)
console.log(test1)
}))
}
test()
console.log("outside")
})
I also tried this way but it still gives me the same error
import './App.css';
import Card from "./Card"
import Books from "./Books"
import { useEffect } from 'react';
import axios from "axios"
import { promised } from 'q';
function App() {
useEffect( ()=>{
async function test(){
const test1= await Promise.all(Books.forEach( (book)=>{
console.log(book + "πΌπΌπ§")
axios.get(`https://openlibrary.org/search.json?q=${book}`)
.then(res=>{
console.log("helooo")
console.log(res)
// console.log(tes)
})
}))
}
test()
console.log("outside")
})
I'd suggest a better approach, with this you can easily catch errors if there were any while making an API call.
try {
const result = await Promise.all(BooksInfo.map( (book)=>{
console.log(book + "πΌπΌπ§")
const test1= axios.get(`https://openlibrary.org/search.json?q=${book}`)
return test1; // This is essential; this is how map func works
}))
console.info("API Result", result);
} catch (error) {
console.error("Network error",error);
}
I think that the problem could be on Books.
You iterate on them while you are exporting it as BooksInfo and importing it as Books, if the import isn't correct Books is null and therefore it triggers the error
I am developing a webapp to visualize some data from a remote csv. I found that react papaparse can help in this. Here is my code
import React, { useContext } from 'react';
import { DataContext } from '../context/DataContext';
import { readRemoteFile } from 'react-papaparse'
const India = () => {
const { data, dispatch } = useContext(DataContext);
const fetchData = () => {
readRemoteFile('https://www.example.com/abc.csv', {
complete: (results) => {
dispatch(
{type:'UPDATE_DATA', data:results}
)
console.log(data)
}
})
}
return(
<div className="container">
{fetchData()}
</div>
)
}
export default India;
When I run this it keeps logging the "data" object infinitely till the server returns 500 error or my system results in some error. Any way to fix it?