From aefd88b5637a51a631b86ff39e79355cec8e1bda Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:03:43 +0000 Subject: [PATCH 1/4] Initial plan From ab51463094acd75675275c0c840eedc168f0efc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:06:25 +0000 Subject: [PATCH 2/4] docs: add useSyncExternalStore reactive auth state section to README Agent-Logs-Url: https://github.com/forwardsoftware/react-auth/sessions/c69b4006-c21b-4061-b105-aa19281aa97b Co-authored-by: panz3r <1754457+panz3r@users.noreply.github.com> --- lib/README.md | 30 ++++++++++++++++++++++++++++++ pnpm-lock.yaml | 17 ++++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/README.md b/lib/README.md index 92696ca..4e80d04 100644 --- a/lib/README.md +++ b/lib/README.md @@ -147,6 +147,36 @@ The `createAuth` function wraps your `AuthClient` implementation with an `Enhanc - `subscribe(() => { })`, subscribe to AuthClient state changes - `getSnapshot()`, returns the current state of the AuthClient +#### Reactive auth state with `useSyncExternalStore` + +`EnhancedAuthClient` is designed to work directly with React's [`useSyncExternalStore`](https://react.dev/reference/react/useSyncExternalStore) hook. This is the **recommended pattern for reading auth state inside components**, especially in multi-component layouts where auth changes must propagate correctly without relying on loading-state workarounds. + +The `subscribe` and `getSnapshot` methods are intentionally declared as bound arrow-function properties so they can be passed **directly** to `useSyncExternalStore` without wrapping: + +```tsx +import { useSyncExternalStore } from 'react'; +import { enhancedAuthClient } from './auth'; // the authClient returned by createAuth + +function MyComponent() { + const { isInitialized, isAuthenticated, tokens } = useSyncExternalStore( + enhancedAuthClient.subscribe, + enhancedAuthClient.getSnapshot, + ); + + if (!isInitialized) return ; + if (!isAuthenticated) return ; + + return ; +} +``` + +The snapshot returned by `getSnapshot()` contains: +- `isInitialized` — `true` once the client's `onInit` hook has completed +- `isAuthenticated` — `true` when the user is logged in and tokens are present +- `tokens` — the current tokens returned by `login` or `refresh`, or `null` when not authenticated + +Any call to `login()`, `refresh()`, or `logout()` triggers a state update and re-renders all components that are subscribed via `useSyncExternalStore`. + --- ### Use multiple AuthClients diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a123ed7..54dd0df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ lockfileVersion: '9.0' settings: - autoInstallPeers: false + autoInstallPeers: true excludeLinksFromLockfile: false catalogs: @@ -148,6 +148,13 @@ importers: version: 4.1.2(@types/node@25.5.1)(jsdom@29.0.1)(vite@8.0.3(@types/node@25.5.1)(terser@5.46.1)(yaml@2.8.2)) packages/google-signin: + dependencies: + expo-modules-core: + specifier: '>=2.0.0' + version: 55.0.20(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react-native: + specifier: '>=0.73.0' + version: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) devDependencies: '@forward-software/react-auth': specifier: ^2.0.0 @@ -493,6 +500,8 @@ packages: '@react-native/codegen@0.84.1': resolution: {integrity: sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==} engines: {node: '>= 20.19.4'} + peerDependencies: + '@babel/core': '*' '@react-native/community-cli-plugin@0.84.1': resolution: {integrity: sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==} @@ -2540,7 +2549,7 @@ snapshots: '@react-native/assets-registry@0.84.1': {} - '@react-native/codegen@0.84.1': + '@react-native/codegen@0.84.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.2 @@ -2549,8 +2558,6 @@ snapshots: nullthrows: 1.1.1 tinyglobby: 0.2.15 yargs: 17.7.2 - transitivePeerDependencies: - - supports-color '@react-native/community-cli-plugin@0.84.1': dependencies: @@ -3870,7 +3877,7 @@ snapshots: dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.84.1 - '@react-native/codegen': 0.84.1 + '@react-native/codegen': 0.84.1(@babel/core@7.29.0) '@react-native/community-cli-plugin': 0.84.1 '@react-native/gradle-plugin': 0.84.1 '@react-native/js-polyfills': 0.84.1 From 1bd96f9948d35b3761ed84d8cac53929d2bae903 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:07:27 +0000 Subject: [PATCH 3/4] docs: fix authClient variable name and revert lockfile changes Agent-Logs-Url: https://github.com/forwardsoftware/react-auth/sessions/c69b4006-c21b-4061-b105-aa19281aa97b Co-authored-by: panz3r <1754457+panz3r@users.noreply.github.com> --- lib/README.md | 6 +++--- pnpm-lock.yaml | 17 +++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/README.md b/lib/README.md index 4e80d04..06e8507 100644 --- a/lib/README.md +++ b/lib/README.md @@ -155,12 +155,12 @@ The `subscribe` and `getSnapshot` methods are intentionally declared as bound ar ```tsx import { useSyncExternalStore } from 'react'; -import { enhancedAuthClient } from './auth'; // the authClient returned by createAuth +import { authClient } from './auth'; // the authClient returned by createAuth function MyComponent() { const { isInitialized, isAuthenticated, tokens } = useSyncExternalStore( - enhancedAuthClient.subscribe, - enhancedAuthClient.getSnapshot, + authClient.subscribe, + authClient.getSnapshot, ); if (!isInitialized) return ; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54dd0df..a123ed7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ lockfileVersion: '9.0' settings: - autoInstallPeers: true + autoInstallPeers: false excludeLinksFromLockfile: false catalogs: @@ -148,13 +148,6 @@ importers: version: 4.1.2(@types/node@25.5.1)(jsdom@29.0.1)(vite@8.0.3(@types/node@25.5.1)(terser@5.46.1)(yaml@2.8.2)) packages/google-signin: - dependencies: - expo-modules-core: - specifier: '>=2.0.0' - version: 55.0.20(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native: - specifier: '>=0.73.0' - version: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) devDependencies: '@forward-software/react-auth': specifier: ^2.0.0 @@ -500,8 +493,6 @@ packages: '@react-native/codegen@0.84.1': resolution: {integrity: sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==} engines: {node: '>= 20.19.4'} - peerDependencies: - '@babel/core': '*' '@react-native/community-cli-plugin@0.84.1': resolution: {integrity: sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==} @@ -2549,7 +2540,7 @@ snapshots: '@react-native/assets-registry@0.84.1': {} - '@react-native/codegen@0.84.1(@babel/core@7.29.0)': + '@react-native/codegen@0.84.1': dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.2 @@ -2558,6 +2549,8 @@ snapshots: nullthrows: 1.1.1 tinyglobby: 0.2.15 yargs: 17.7.2 + transitivePeerDependencies: + - supports-color '@react-native/community-cli-plugin@0.84.1': dependencies: @@ -3877,7 +3870,7 @@ snapshots: dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.84.1 - '@react-native/codegen': 0.84.1(@babel/core@7.29.0) + '@react-native/codegen': 0.84.1 '@react-native/community-cli-plugin': 0.84.1 '@react-native/gradle-plugin': 0.84.1 '@react-native/js-polyfills': 0.84.1 From 14183ce11010cb94882caeb41c466721d7d98e25 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:28:51 +0000 Subject: [PATCH 4/4] docs: fix heading level, tokens description, and re-render trigger accuracy Agent-Logs-Url: https://github.com/forwardsoftware/react-auth/sessions/bf046831-969a-4a72-bd88-914b50836046 Co-authored-by: panz3r <1754457+panz3r@users.noreply.github.com> --- lib/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/README.md b/lib/README.md index 06e8507..11388e7 100644 --- a/lib/README.md +++ b/lib/README.md @@ -147,7 +147,7 @@ The `createAuth` function wraps your `AuthClient` implementation with an `Enhanc - `subscribe(() => { })`, subscribe to AuthClient state changes - `getSnapshot()`, returns the current state of the AuthClient -#### Reactive auth state with `useSyncExternalStore` +##### Reactive auth state with `useSyncExternalStore` `EnhancedAuthClient` is designed to work directly with React's [`useSyncExternalStore`](https://react.dev/reference/react/useSyncExternalStore) hook. This is the **recommended pattern for reading auth state inside components**, especially in multi-component layouts where auth changes must propagate correctly without relying on loading-state workarounds. @@ -173,9 +173,9 @@ function MyComponent() { The snapshot returned by `getSnapshot()` contains: - `isInitialized` — `true` once the client's `onInit` hook has completed - `isAuthenticated` — `true` when the user is logged in and tokens are present -- `tokens` — the current tokens returned by `login` or `refresh`, or `null` when not authenticated +- `tokens` — the current tokens object returned by `login` or `refresh`; when no tokens are available (e.g. before login or after logout), this is an empty object -Any call to `login()`, `refresh()`, or `logout()` triggers a state update and re-renders all components that are subscribed via `useSyncExternalStore`. +Successful `login()`, `refresh()`, `logout()`, and initialization update the auth state and re-render all components that are subscribed via `useSyncExternalStore`. ---