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`.
---