Skip to content

Commit

Permalink
Move useIsSignedIn into a useAuth hook with { user, session, isSigned…
Browse files Browse the repository at this point in the history
…In } properties
  • Loading branch information
kristianpd committed Jul 5, 2023
1 parent 43aa77f commit a3c4095
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 74 deletions.
18 changes: 12 additions & 6 deletions packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1078,13 +1078,19 @@ Returns the current session, equivalent to `await api.currentSession.get()` or `
### `useUser()`
Returns the current user of the session, if present. For unauthenticated sessions, returns `null`. Throws a Suspense promise while the session/user are loading
### `useIsSignedIn()`
Returns true if the session is logged in (has a user associated with it), and false otherwise. Throws a Suspense promise while the session/user is loading
### `useAuth()`
Returns an object representing the current authentication state of the session. Throws a Suspense promise while the session is being loaded.
`user` - the current `User`, if signed in. similar to `useUser`
`session` - the current `Session`. similar to `useSession`
`isSignedIn` - set to `true` if the session has a user associated with it (signed in), `false` otherwise
```tsx
export default function App() {
const user = useUser();
const isSignedIn = useIsSignedIn();
const { isSignedIn } = useAuth();
const gadgetContext = useGadgetContext();

return (
Expand All @@ -1100,20 +1106,20 @@ export default function App() {
If you are trying to control the layout of your application based on authentication state, it may be helpful to use the Gadget auth React components instead of, or in addition to, the hooks.
### `<SignedIn />`
Conditionally renders its children if the current session has a user associated with it, similar to the `useIsSignedIn()` hook.
Conditionally renders its children if the current session has a user associated with it, similar to the `isSignedIn` property of the `useAuth()` hook.
```tsx
<h1>Hello<SignedIn>, human</SignedIn>!</h1>
```
### `<SignedOut />`
Conditionally renders its children if the current session has a user associated with it, similar to the `useIsSignedIn()` hook.
Conditionally renders its children if the current session has a user associated with it.
```tsx
<SignedOut><a href="/auth/signin">Sign In!</a></SignedOut>
```
### <SignedInOrRedirect />
### `<SignedInOrRedirect />`
Conditionally renders its children if the current session has a user associated with it, or redirects the browser via `window.location.assign` if the user is not currently signed in. This component is helpful for protecting front-end routes.
```tsx
Expand Down
52 changes: 52 additions & 0 deletions packages/react/spec/auth/useAuth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { renderHook } from "@testing-library/react";
import { useAuth } from "../../src/auth/useAuth";
import { superAuthApi } from "../apis";
import { TestWrapper } from "../testWrapper";
import { expectMockSignedInUser, expectMockSignedOutUser, mockInternalServerError, mockNetworkError } from "../utils";

describe("useAuth", () => {
test("returns the correct auth state if the user is signed in", async () => {
const { result, rerender } = renderHook(() => useAuth(), { wrapper: TestWrapper(superAuthApi) });

expectMockSignedInUser();

rerender();
expect(result.current.isSignedIn).toBe(true);
expect(result.current.session.id).toBe("123");
expect(result.current.user!.id).toBe("321");
});

test("returns the correct auth state if the user is signed out", async () => {
const { result, rerender } = renderHook(() => useAuth(), { wrapper: TestWrapper(superAuthApi) });

expectMockSignedOutUser();

rerender();
expect(result.current.isSignedIn).toBe(false);
expect(result.current.session.id).toBe("123");
expect(result.current.user).toBe(null);
});

test("it throws when the server responds with an error", async () => {
expect(() => {
const { rerender } = renderHook(() => useAuth(), { wrapper: TestWrapper(superAuthApi) });

mockInternalServerError();

rerender();
}).toThrowErrorMatchingInlineSnapshot(`
"[GraphQL] GGT_INTERNAL_SERVER_ERROR
[GraphQL] An error occurred"
`);
});

test("it throws when request fails to complete", async () => {
expect(() => {
const { rerender } = renderHook(() => useAuth(), { wrapper: TestWrapper(superAuthApi) });

mockNetworkError();

rerender();
}).toThrowErrorMatchingInlineSnapshot(`"[Network] Network error"`);
});
});
48 changes: 0 additions & 48 deletions packages/react/spec/auth/useIsSignedIn.spec.ts

This file was deleted.

28 changes: 20 additions & 8 deletions packages/react/spec/auth/useSession.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ describe("useSession", () => {
expectMockSignedInUser();
rerender();

expect(result.current!.id).toEqual("123");
expect(result.current!.userId).toEqual("321");
expect(result.current!.user!.id).toEqual("321");
expect(result.current!.user!.firstName).toEqual("Jane");
expect(result.current!.user!.lastName).toEqual("Doe");
expect(result.current.id).toEqual("123");
expect(result.current.userId).toEqual("321");
expect(result.current.user!.id).toEqual("321");
expect(result.current.user!.firstName).toEqual("Jane");
expect(result.current.user!.lastName).toEqual("Doe");
});

test("it returns the current session when the user is logged out", async () => {
Expand All @@ -25,9 +25,21 @@ describe("useSession", () => {
rerender();

expect(result.current).toBeDefined();
expect(result.current!.id).toEqual("123");
expect(result.current!.userId).toBe(null);
expect(result.current!.user).toBe(null);
expect(result.current.id).toEqual("123");
expect(result.current.userId).toBe(null);
expect(result.current.user).toBe(null);
});

test("it returns the current session when the user is logged out", async () => {
const { result, rerender } = renderHook(() => useSession(), { wrapper: TestWrapper(superAuthApi) });

expectMockSignedOutUser();
rerender();

expect(result.current).toBeDefined();
expect(result.current.id).toEqual("123");
expect(result.current.userId).toBe(null);
expect(result.current.user).toBe(null);
});

test("it throws when the server responds with an error", async () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/react/src/auth/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useSession } from "./useSession";
import { useUser } from "./useUser";

/**
* Used for fetching the current authentication state of the session
* @returns An object with the current authentication state: `session`, `user`, and `isSignedIn`
*/
export const useAuth = () => {
const session = useSession();
const user = useUser();
return { session, user, isSignedIn: !!user };
};
10 changes: 0 additions & 10 deletions packages/react/src/auth/useIsSignedIn.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/react/src/auth/useSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ const useGetSessionAndUser = () => {
* Used for fetching the current `Session` record from Gadget. Will suspend while the user is being fetched.
* @returns The current session
*/
export const useSession = (): GadgetSession | undefined => {
export const useSession = (): GadgetSession => {
const [{ data: session, error }] = useGetSessionAndUser();
if (error) throw error;
if (!session) throw new Error("currentSession not found but should be present");
return session;
};
2 changes: 1 addition & 1 deletion packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { Consumer, Context } from "urql";
export { Provider } from "./GadgetProvider";
export * from "./auth/useIsSignedIn";
export * from "./auth/useAuth";
export * from "./auth/useSession";
export * from "./auth/useUser";
export * from "./components/auth/SignedIn";
Expand Down

0 comments on commit a3c4095

Please sign in to comment.