S
SolidJS3mo ago
expert

Canonical way to handle exceptions in async functions to perform redirect?

Hi folks, I'm very new to solid and frontend development hence my question. I have a class that talks to server API. It uses cookie to authenticate so eventually cookie may expire and an endpoint returns http 401 error.
export class UserActions {

private handleError(response: Response): void {
if (response.status === 401) {
useNavigate()(`/login?redirect=${encodeURIComponent(location.pathname)}`)
// throw new ServiceError(ErrorType.Authentication, 'Please login again');
}
}

login(email: string, password: string): Promise<User | null> {

return fetch('http://localhost:8080/api/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
password: password,
}),
})
.then(response => {
this.handleError(response);
return response.ok ? response.json() : null;
})
}
}
export class UserActions {

private handleError(response: Response): void {
if (response.status === 401) {
useNavigate()(`/login?redirect=${encodeURIComponent(location.pathname)}`)
// throw new ServiceError(ErrorType.Authentication, 'Please login again');
}
}

login(email: string, password: string): Promise<User | null> {

return fetch('http://localhost:8080/api/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
password: password,
}),
})
.then(response => {
this.handleError(response);
return response.ok ? response.json() : null;
})
}
}
and here is example how it's used
export default function UserProfile() {
const context = useContext(SessionContext);

const [read, write] = createSignal<string>("Initial value")

const clickHandler = async () => {
let user = await api.currentUser()
write(user.email)
};

return (
<AuthenticatedRoute>
<div>Hello, {context?.user?.firstName} {context?.user?.lastName}</div><br/>
<button onClick={clickHandler}>Refresh</button><br/>
<div>Value: {read()}</div>
</AuthenticatedRoute>
);
}
export default function UserProfile() {
const context = useContext(SessionContext);

const [read, write] = createSignal<string>("Initial value")

const clickHandler = async () => {
let user = await api.currentUser()
write(user.email)
};

return (
<AuthenticatedRoute>
<div>Hello, {context?.user?.firstName} {context?.user?.lastName}</div><br/>
<button onClick={clickHandler}>Refresh</button><br/>
<div>Value: {read()}</div>
</AuthenticatedRoute>
);
}
How can I centralize handing of such error so that any page that gets the exception would redirect user to login page ? I tried using <ErrorBoundary> but it doesn't catch exception thrown in onClick handler because, I assume, it's not thrown in rendering stage. I tried calling navigate() in UserActions but it does nothing. What is the proper way to achieve it ? Thank you.
1 Reply
expert
expert3mo ago
Apparently I have to wrap async calls into [createResource][1] to make its exceptions to be catchable in [ErrorBoundary.fallback][2]. That was my page become something like this
export default function UserProfile() {
const context = useContext(SessionContext);

const [data, { mutate, refetch }] = createResource(() => api.currentUser())

return (
<AuthenticatedRoute>
<div>Hello, {context?.user?.firstName} {context?.user?.lastName}</div><br/>
<button onClick={refetch}>Refresh</button><br/>
<Suspense>
<div>Value: {data()?.email}</div>
</Suspense>
</AuthenticatedRoute>
);
}
export default function UserProfile() {
const context = useContext(SessionContext);

const [data, { mutate, refetch }] = createResource(() => api.currentUser())

return (
<AuthenticatedRoute>
<div>Hello, {context?.user?.firstName} {context?.user?.lastName}</div><br/>
<button onClick={refetch}>Refresh</button><br/>
<Suspense>
<div>Value: {data()?.email}</div>
</Suspense>
</AuthenticatedRoute>
);
}
If there is better way, pleas elet me know.