react-query with graphql-request: Error boundary not running - reactjs

I have the following code:
const queryClient = new QueryClient({
defaultOptions: {
useErrorBoundary: true,
queries: {
suspense: true,
useErrorBoundary: true,
retry: 0,
}
}
});
const useUsers = () => {
return useQuery("users", async () => {
const users = await fetchUsers();
console.log(users);
return users;
})
};
function UserList() {
const { data, isFetching, error, status } = useUsers();
const { users } = data;
// return some render with users
}
My fetchUsers method:
export function fetchUsers(fields = ['id', 'name']) {
console.info("fetch users");
return request(`${process.env.REACT_APP_SERVER_URL}/graphql`, gql`
query getUsers {
users {
${fields},nice
}
}`);
}
My App.js:
<QueryClientProvider client={queryClient}>
<ErrorBoundary
fallbackRender={({ error }) => (
<div>
There was an error!{" "}
<pre style={{ whiteSpace: "normal" }}>{error.message}</pre>
</div>
)}
onReset={() => {
// reset the state of your app so the error doesn't happen again
}}
>
<UserList/>
</ErrorBoundary>
</QueryClientProvider>
I expect to see the ErrorBoundary running when I type unexisting graphql field (aka nice) - but my react app crashes with:
Error: Cannot query field "nice" on type "User". Did you mean "name"?
Why the error boundary don't catch this error? Any idea what I'm missing?

Have you set useErrorBoundary: true to turn on that option, because I’m not seeing that on your code

Related

experimental next.js 13 map is not a function

I am using the experimental next.js (ver.13)
Trying to understand what is wrong with my code .. as i am not getting anything and i am simply following the documentation.
it errors : photos.map is not a function
async function getPhotos() {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
return res.json();
}
export default function Photos() {
const photos = getPhotos();
return (
<div>
{photos.map((photo) => {
return <div key={photo.id}>{photo.title}</div>;
})}
</div>
);
}
Make the Photos component async aswell. It's also a good idea to use conditional chaining ?. on the photos array, so if in any case it is undefined, it doesn't throw an error
async function getPhotos() {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
return res.json();
}
export default async function Photos() {
const photos = await getPhotos();
return (
<div>
{photos?.map((photo) => {
return <div key={photo.id}>{photo.title}</div>;
})}
</div>
);
}
from here
if your node version is above 18
module.exports = {
experimental: {
enableUndici: true
}
}

getServerSideProps not working in nextjs error in mapping

I am trying to do getServerSideProps but I am getting the following error what is the error I am doing
TypeError: Cannot read properties of undefined (reading 'map')
import React from "react";
import axios from "axios";
import { useState, useEffect } from "react";
import { FormControl, Button } from "react-bootstrap";
import Card from "react-bootstrap/Card";
export default function Answershooks(props, { posts }) {
return (
<div className="answerhook">
{posts.map((personData, index) => {
return (
<Card key={index} className="cardmobile">
<Card.Body>
<p className="answersize">{personData.Answers} </p>
</Card.Body>
</Card>
);
})}
</div>
);
}
export async function getServerSideProps(ctx) {
const res = await fetch("https://askover.wixten.com/answersapi/" + props.id);
console.log(res);
console.log("dada");
const posts = await res.json();
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
};
}
i have added added a file stucture screenshot so u undersand how my files are placed
Your main problem is you're trying to call getServerSideProps in Answerhooks but it's not a page component, so you cannot get data on the server as expected
Instead of having getServerSideProps in that, you can move your API call to getServerSideProps in [itmid].jsx (which is an actual page component) like below
export async function getServerSideProps(ctx) {
var id = ctx.query.itmid;
const queryRequest = fetch("https://ask-over.herokuapp.com/questone/" + id).then(async (res) => await res.json());
const answerRequest = fetch("https://askover.wixten.com/answersapi/" + id).then(async (res) => await res.json());
const [posts, answerPosts] = await Promise.all([queryRequest, answerRequest]);
return {
props: {
posts,
answerPosts
}
};
}
After that, you can get answerPosts from props for Query
function Query({ posts, answerPosts }) {
return <Answerhooks answerPosts={answerPosts} />
}
Finally, you can have the data on props inside Answerhooks component
function Answershooks({ answerPosts }) {
//TODO: Apply your logic with `answerPosts`
console.log(answerPosts)
return <div></div>
}
Lets start with the fetch error and work out why that is failing so make a new component.
fetchHandler.js
export async function fetchHandler(url){
try{
const res = await fetch(url);
return res
} catch(err){
console.log(err); //this will tell us why it failed.
return false //this gives us a condition we can use on the front end
}
}
Then your Answerhooks.
import {fetchHandler} from '../yourpath'
export default function Answershooks({ posts }) {
return (
<div className="answerhook">
{posts.map((personData, index) => {
return (
<Card key={index} className="cardmobile">
<Card.Body>
<p className="answersize">{personData.Answers} </p>
</Card.Body>
</Card>
);
})}
</div>
);
}
export async function getServerSideProps(ctx) {
const url = `https://askover.wixten.com/answersapi/${ctx.query.id}`
const res = await fetchHandler(url)
console.log(res);
const posts = await res.json();
return {
props: {
posts: posts === false ? [] : posts //Just to stop your app crashing
},
};
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({req}) => {
const result = await store.dispatch(fetchHome());
return {
props: {
list : result
},
};
}
);

Why is the API data not transferring to my other module? React

For this project, I am attempting to transfer the data I have received from the API (a list of albums) and send them to another module in order to display the results based on whichever user has been selected. I'm using "useEffect()" in tandem with the "setAlbums()" function to set and send the album list data through a prop labeled "album". The problem is I am not receiving the data in the other module "AlbumList.js", so I can't display the user's album list. Can anyone find a solution to this? Please forgive all the logs, I'm new to working with React and have been trying to sort this out. Thanks so much for taking the time.
Here is the App.js file:
// App.js
import "./App.css";
import AlbumList from "./AlbumList";
import UserList from "./UserList";
function App() {
const controller = new AbortController();
const [users, setUsers] = useState([]);
const [user, setCurrentUser] = useState({});
const [albums, setAlbums] = useState([]);
document.title = 'Awesome Album App';
const userUrl = "https://jsonplaceholder.typicode.com/users";
// Loading Albums
useEffect(() => {
const albumUrl = `https://jsonplaceholder.typicode.com/albums?userId=${user.id}`;
async function loadAlbums() {
try {
const response = await fetch(albumUrl, { signal: controller.signal });
const json = await response.json();
console.log("Logging json: ", json)
setAlbums(json)
} catch (err) {
if (err.name === 'AbortError') {
console.log('Aborted', err)
} else {
throw err;
}
}
}
loadAlbums();
console.log("After loadAlbums: ", albums)
return () => {
controller.abort();
}
},[user])
// Loading Users
useEffect(() => {
async function loadUser() {
try {
const response = await fetch(userUrl, { signal: controller.signal });
const data = await response.json();
setUsers(...users, data);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Aborted', err)
setUsers([])
} else {
throw err;
}
}
}
loadUser();
return () => {
controller.abort();
}
},[])
// Return JSX
return (
<div className="App">
<div className="left column">
<UserList users={users} setCurrentUser={setCurrentUser} />
</div>
<div className="right column">
<AlbumList user={user} album={album} />
</div>
</div>
);
}
export default App;
Here is the component that displays the albums:
// AlbumList.js
import React from "react";
function AlbumList({ user = {} }, albums) {
console.log("Logging inside of albumsList", albums)
if (albums.length) {
return albums.map((album, index) => {
return <li key={index}>{album.id}{album.title}</li>;
});
}
return (
<div>
<p>Please click on a user name to the left</p>
</div>
);
}
export default AlbumList;```
Is it because you are passing album={album} to the <AlbumList /> component when it should be albums={albums}? I'm not sure if this was just an error when you were transferring your code to Stack Overflow, but <AlbumList/> expects a prop of albums whereas you passed an album prop. Though, I'm curious as to why the compiler didn't throw an error for album not being defined - from what I can see, you only have the albums variable defined. Also, I believe you need to destructure the props in AlbumList.js like so
function AlbumList({ user = {} , albums}) {
(i.e. } should appear after the albums prop).

Reactjs : error boundary with mobx async call

Hello I'm trying to implement error boundary to my system, but I'm having problems I have an asynchronous call to my api, to check if the backend is up and if the browser session is auth, for testing I left my backend on, but my fallback was not called, I'm using lib: react-error-boundary
If anyone can help me with this I am grateful
error:
Store:
public initApi = async (): Promise<void> => {
this.appState = 'pending'
try {
const response = await this.AxiosStore.get('/')
if (!response) return Promise.reject(new Error('Service Unavaliable'))
return runInAction(() => {
if (response.data.acess_token)
this.currentUserStore.accessToken = response.data.access_token
this.appState = 'fulfilled'
})
} catch (error) {
runInAction(() => {
this.appState = 'error'
})
return Promise.reject(error)
}
}
}
App:
const AuthApp: React.FC<{
isAuth: boolean
}> = observer(({isAuth}) => {
return (
<>
{isAuth ? (
<Suspense fallback={<h1>fb</h1>}>
<DashBoard />
</Suspense>
) : (
<Suspense fallback={<h1>login</h1>}>
<LoginPage />
</Suspense>
)}
</>
)
})
const App: React.FC = observer(() => {
const {
layoutStore,
initApi,
appState,
currentUserStore,
authStore,
} = useRootStore()
const handleError = useErrorHandler()
useEffect(() => {
if (appState !== 'fulfilled') initApi().catch((error) => handleError(error))
}, [])
return (
<ThemeProvider theme={layoutStore.isDarkMode ? darkTheme : lightTheme}>
<GlobalReset />
<ErrorBoundary FallbackComponent={ErrorFallback}>
{appState === 'fulfilled' ? <AuthApp isAuth={authStore.isAuth} /> : 'b'}
</ErrorBoundary>
</ThemeProvider>
)
})
You need to handle the async scenario with error, react-error-boundary has a special hook for that.
// create the hook
const handleError = useErrorHandler() //hook from the react-error-boundary
// pass error in the catch block to the hook
yourAsyncFunction().catch(e => handleError(e))
Or with try/catch:
try{
await yourAsyncFunction()
}
catch(e){
handleError(e)
}

Converting a class based component to hooks (gapi API)

I have this class based component using the gapi (Google Auth) API that renders a button and it works:
import React from 'react';
class GoogleAuth extends React.Component {
state = { isSignedIn: null };
componentDidMount() {
window.gapi.load('client:auth2', () => {
window.gapi.client
.init({
clientId: process.env.REACT_APP_CLIENT_ID,
scope: 'email',
})
.then(() => {
this.auth = window.gapi.auth2.getAuthInstance();
this.handleAuthChange();
this.auth.isSignedIn.listen(this.handleAuthChange);
});
});
}
handleAuthChange = () => {
this.setState({ isSignedIn: this.auth.isSignedIn.get() });
};
handleSignIn = () => {
this.auth.signIn();
};
handleSignOut = () => {
this.auth.signOut();
};
renderAuthButton() {
if (this.state.isSignedIn === null) {
return null;
} else if (this.state.isSignedIn) {
return <button onClick={this.handleSignOut}>Sign Out</button>;
} else {
return <button onClick={this.handleSignIn}>Sign in with Google</button>;
}
}
render() {
return <div>{this.renderAuthButton()}</div>;
}
}
export default GoogleAuth;
I'm having a tough time trying to convert this to use hooks. The main issue is this.auth... That's how the class has a reference to window.gapi.auth2.getAuthInstance()
I have tried many different ways including keeping auth in state like:
export default function GoogleAuth() {
const [isSignedIn, setIsSignedIn] = useState(null);
const [auth, setAuth] = useState(null);
useEffect(() => {
window.gapi.load('client:auth2', () => {
window.gapi.client
.init({
clientId: process.env.REACT_APP_CLIENT_ID,
scope: 'email',
})
.then(() => {
setAuth(window.gapi.auth2.getAuthInstance());
setIsSignedIn(auth.isSignedIn.get());
auth.isSignedIn.listen(() => setIsSignedIn(auth.isSignedIn.get()));
});
});
}, [auth]);
It's only 8 months later but try useRef with auth like below. It works for me.
const GoogleAuth = () => {
const [isSignedIn, setSignedIn] = useState(null)
const auth = useRef(null);
useEffect(() => {
window.gapi.load('client:auth2', () => {
window.gapi.client.init({
clientId:
'jcu.apps.googleusercontent.com',
scope: 'email'
}).then(() => {
auth.current = window.gapi.auth2.getAuthInstance();
setSignedIn(auth.current.isSignedIn.get());
auth.current.isSignedIn.listen(onAuthChange)
});
});
}, [isSignedIn]);
const onAuthChange = () => {
setSignedIn(auth.current.isSignedIn.get())
}
if (isSignedIn === null) {
return (
<div>I don't know if we are signed in!</div>
);
} else if ( isSignedIn ){
return (
<div>I am signed in!</div>
);
} else {
return ( <div>I am not signed in. :(</div>);
}
}
Couple issues - you're referencing auth immediately after you're setting the state - auth won't be set until it re-renders with its new state.
I'm playing with similar code, and I had to resort to utilizing window.gapi in the initial setup to properly access the returned auth instance.
I imagine it may throw an error if a user clicks quickly they could catch it before auth is set, but I found the sign in/out functions to be able to handle this.
I also found it easiest to test in Incognito, as cookies and cacheing of the api seemed to create an unpredictable local testing environment.
My current component state
Just line in useEffect after auth.current = ...
setSignedIn(auth.current.isSignedIn.get()); is de facto function onAuthChange so call it like that:
.then(() => {
auth.current = window.gapi.auth2.getAuthInstance();
onAuthChange();
auth.current.isSignedIn.listen(onAuthChange);
});

Resources