Skip to content

Add an enabled prop to KnockProvider#1033

Draft
kylemcd wants to merge 6 commits into
mainfrom
kyle-kno-14095-add-enabled-prop-to-knockprovider-full-implementation-plan
Draft

Add an enabled prop to KnockProvider#1033
kylemcd wants to merge 6 commits into
mainfrom
kyle-kno-14095-add-enabled-prop-to-knockprovider-full-implementation-plan

Conversation

@kylemcd

@kylemcd kylemcd commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

Adds an enabled prop to <KnockProvider>. When it's false, children still render but the Knock client sits idle: no identify call, no API requests, no websocket. Set it to true and it connects like a login; set it back to false and it shuts down like a logout. It defaults to true, so existing code is unaffected.

Today, if you don't have a user yet (common when the user token loads asynchronously), your only option is to conditionally mount the provider. This replaces that:

<KnockProvider apiKey={apiKey} user={{ id: userId }} userToken={userToken} enabled={Boolean(userId && userToken)} />

The prop itself is small. Most of the work is that a lot of the SDK used to throw, or fire requests with userId=undefined, when no user was set. The worst case: rendering Guides before a user was set would throw and crash the app. This PR makes all of those paths do nothing when there's no user.

Four commits, one per area:

  1. @knocklabs/client — With no signed-in user, feed actions, Guides, and Slack/Teams checks now no-op instead of throwing or calling the API. Adds knock.logout() (disconnects the websocket, clears the token-refresh timer, removes listeners) and knock.authStatus / knock.authStore so you can check or subscribe to whether a user is signed in. Also fixes two existing Guide bugs: real-time updates breaking after a re-login, and a location-tracking patch that broke when a Guide provider remounted.
  2. @knocklabs/react-core — The enabled prop. When it's off, the provider passes no user, so the client stays logged out; toggling it rebuilds a fresh client so the feed reloads on login and clears out on logout. Also stops useFeedSettings from calling /v1/users/undefined/..., and disconnects the client when the provider unmounts.
  3. react-core + expo — New useKnockAuthState() hook. Slack/Teams connection status now re-checks when the user changes (before, it checked once and stuck with that result). Expo waits for a signed-in user before registering for push, so logged-out users don't see the OS permission prompt.
  4. Docs — READMEs and JSDoc for the enabled prop.

Changesets are included for every affected package. The full test suite passes (981 tests). Replaces #820, which was stale and had a bug where the feed never loaded after enabling.

Todos

Checklist

  • Tests have been added for new features or major refactors to existing features.

kylemcd added 4 commits July 9, 2026 14:25
Add a subscribable authStore + authStatus getter and a Knock.logout() that
tears down all stateful connections (socket, token-expiration timer, and
page-visibility listener) and lazily re-creates the API client.

Make user-scoped calls quiescent while unauthenticated instead of throwing
or firing blind:
- Feed markAs*/markAll*/fetchNextPage no-op without an optimistic store write.
- Guide fetch/subscribe/step-marks no longer throw (fixes the crash when
  Guides render before a user is set).
- Slack/MS Teams authCheck return a disconnected shape; getChannels/getTeams
  return empty; messages.batchUpdateStatuses returns [].

Also fix two guide bugs the enabled prop exacerbates: re-read the socket from
the API client on each subscribe so real-time survives re-auth, and share the
history pushState/replaceState patch per window so remounting a guide provider
no longer nests patches or leaves the originals unrestored.
Implement `enabled` (default true) as credential-nulling through
useAuthenticatedKnockClient: when false the client is created but left
unauthenticated and fully quiescent while children still render. A fresh
client is built on every enable/disable transition so enabling remounts the
feed subtree (and refetches) and disabling clears the previous user's stores.

Also guard useFeedSettings against firing GET /v1/users/undefined/.../settings
for an unauthenticated user, and tear the client down on provider unmount
(StrictMode-safe via a dispose/re-init flag) instead of leaking the socket,
token timer, and page-visibility listener.
Add useKnockAuthState(knock), which subscribes to the client's authStore and
re-renders on login, logout, or a user switch.

Wire it into the integrations that previously latched their state:
- Slack/MS Teams connection status resets and re-runs authCheck when the
  authenticated user changes, and the provider keys now include the userId so
  a user switch reliably re-renders consumers.
- Expo autoRegister waits for an authenticated user before registering a push
  token, deferring the OS permission prompt for logged-out users and
  re-registering on sign-in; a notification tapped while logged out no longer
  fires a message-status update.
Add the readyToTarget x enabled/auth matrix to KnockGuideProvider's JSDoc, and
document the enabled prop in the react, react-native, and expo READMEs plus the
vanilla logout/quiescence story in the client README.
@linear-code

linear-code Bot commented Jul 9, 2026

Copy link
Copy Markdown

KNO-14095

@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 77d1f6c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@knocklabs/client Minor
@knocklabs/react-core Minor
@knocklabs/react Minor
@knocklabs/react-native Minor
@knocklabs/expo Minor
client-example Patch
guide-example Patch
ms-teams-connect-example Patch
nextjs-app-dir-example Patch
nextjs-example Patch
slack-connect-example Patch
slack-kit-example Patch
@knocklabs/expo-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
javascript-ms-teams-connect-example Ready Ready Preview, Comment Jul 9, 2026 8:59pm
javascript-nextjs-example Ready Ready Preview, Comment Jul 9, 2026 8:59pm
javascript-slack-connect-example Ready Ready Preview, Comment Jul 9, 2026 8:59pm
javascript-slack-kit-example Ready Ready Preview, Comment Jul 9, 2026 8:59pm

Request Review

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 11.39kB (2.03%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
@knocklabs/react-core-react-core-cjs 37.93kB 1.48kB (4.07%) ⬆️
@knocklabs/expo-expo-cjs 7.57kB 161 bytes (2.17%) ⬆️
@knocklabs/expo-expo-esm 8.16kB 240 bytes (3.03%) ⬆️
@knocklabs/client-client-esm 87.02kB 5.03kB (6.14%) ⬆️
@knocklabs/react-core-react-core-esm 43.15kB 1.6kB (3.84%) ⬆️
@knocklabs/client-client-cjs 63.3kB 2.88kB (4.77%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: @knocklabs/client-client-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
clients/guide/client.js 687 bytes 18.35kB 3.89%
clients/feed/feed.js 634 bytes 10.74kB 6.27% ⚠️
knock.js 646 bytes 4.2kB 18.16% ⚠️
clients/ms-*.js 441 bytes 2.38kB 22.79% ⚠️
clients/slack/index.js 296 bytes 1.89kB 18.55% ⚠️
clients/messages/index.js 128 bytes 1.62kB 8.55% ⚠️
clients/feed/index.js 50 bytes 1.21kB 4.33%

Files in clients/guide/client.js:

  • ./src/clients/guide/client.ts → Total Size: 30.22kB

Files in clients/feed/feed.js:

  • ./src/clients/feed/feed.ts → Total Size: 18.26kB

Files in knock.js:

  • ./src/knock.ts → Total Size: 7.01kB

Files in clients/ms-*.js:

  • ./src/clients/ms-teams/index.ts → Total Size: 3.21kB

Files in clients/slack/index.js:

  • ./src/clients/slack/index.ts → Total Size: 2.4kB

Files in clients/messages/index.js:

  • ./src/clients/messages/index.ts → Total Size: 2.0kB

Files in clients/feed/index.js:

  • ./src/clients/feed/index.ts → Total Size: 1.22kB
view changes for bundle: @knocklabs/react-core-react-core-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
index.mjs 113 bytes 4.2kB 2.76%
modules/slack/hooks/useSlackAuth.mjs 32 bytes 2.11kB 1.54%
modules/slack/hooks/useSlackChannels.mjs 32 bytes 2.06kB 1.58%
modules/ms-*.mjs 219 bytes 1.56kB 16.29% ⚠️
modules/ms-*.mjs 32 bytes 2.01kB 1.62%
modules/ms-*.mjs 32 bytes 1.6kB 2.04%
modules/ms-*.mjs 54 bytes 1.39kB 4.05%
modules/ms-*.mjs 32 bytes 1.38kB 2.37%
modules/ms-*.mjs 32 bytes 1.93kB 1.69%
modules/slack/hooks/useConnectedSlackChannels.mjs 32 bytes 1.9kB 1.71%
modules/slack/hooks/useSlackConnectionStatus.mjs 219 bytes 1.81kB 13.8% ⚠️
modules/slack/context/KnockSlackProvider.mjs 54 bytes 1.52kB 3.68%
modules/guide/context/KnockGuideProvider.mjs 32 bytes 1.37kB 2.4%
modules/core/utils.mjs 32 bytes 1.25kB 2.64%
modules/core/hooks/useAuthenticatedKnockClient.mjs 220 bytes 1.23kB 21.7% ⚠️
modules/core/context/KnockProvider.mjs 65 bytes 1.14kB 6.02% ⚠️
modules/feed/hooks/useNotifications.mjs 32 bytes 1.1kB 3.0%
modules/feed/context/KnockFeedProvider.mjs 32 bytes 1.08kB 3.05%
modules/preferences/hooks/usePreferences.mjs 32 bytes 926 bytes 3.58%
modules/feed/hooks/useFeedSettings.mjs 60 bytes 751 bytes 8.68% ⚠️
modules/core/hooks/useKnockAuthState.mjs (New) 208 bytes 208 bytes 100.0% 🚀

Files in index.mjs:

  • ./src/index.ts → Total Size: 0 bytes

Files in modules/guide/context/KnockGuideProvider.mjs:

  • ./src/modules/guide/context/KnockGuideProvider.tsx → Total Size: 1.48kB

Files in modules/core/utils.mjs:

  • ./src/modules/core/utils.ts → Total Size: 1.46kB

Files in modules/core/hooks/useAuthenticatedKnockClient.mjs:

  • ./src/modules/core/hooks/useAuthenticatedKnockClient.ts → Total Size: 1.88kB

Files in modules/core/context/KnockProvider.mjs:

  • ./src/modules/core/context/KnockProvider.tsx → Total Size: 1.12kB

Files in modules/feed/hooks/useFeedSettings.mjs:

  • ./src/modules/feed/hooks/useFeedSettings.ts → Total Size: 917 bytes

Files in modules/core/hooks/useKnockAuthState.mjs:

  • ./src/modules/core/hooks/useKnockAuthState.ts → Total Size: 91 bytes
view changes for bundle: @knocklabs/expo-expo-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
modules/push/KnockExpoPushNotificationProvider.js 161 bytes 2.78kB 6.14% ⚠️

Files in modules/push/KnockExpoPushNotificationProvider.js:

  • ./src/modules/push/KnockExpoPushNotificationProvider.tsx → Total Size: 5.16kB
view changes for bundle: @knocklabs/react-core-react-core-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
index.js 101 bytes 3.82kB 2.72%
modules/slack/hooks/useSlackAuth.js 33 bytes 1.71kB 1.97%
modules/slack/hooks/useSlackChannels.js 33 bytes 1.69kB 1.99%
modules/ms-*.js 33 bytes 1.41kB 2.4%
modules/ms-*.js 33 bytes 1.25kB 2.7%
modules/ms-*.js 33 bytes 1.15kB 2.96%
modules/ms-*.js 33 bytes 1.63kB 2.07%
modules/ms-*.js 175 bytes 1.15kB 17.95% ⚠️
modules/ms-*.js 49 bytes 1.52kB 3.34%
modules/slack/context/KnockSlackProvider.js 49 bytes 1.55kB 3.27%
modules/guide/context/KnockGuideProvider.js 33 bytes 1.42kB 2.38%
modules/slack/hooks/useConnectedSlackChannels.js 33 bytes 1.4kB 2.42%
modules/slack/hooks/useSlackConnectionStatus.js 175 bytes 1.3kB 15.49% ⚠️
modules/core/context/KnockProvider.js 55 bytes 1.3kB 4.41%
modules/feed/context/KnockFeedProvider.js 33 bytes 1.29kB 2.62%
modules/core/hooks/useAuthenticatedKnockClient.js 164 bytes 1.09kB 17.71% ⚠️
modules/core/utils.js 22 bytes 1.09kB 2.07%
modules/preferences/hooks/usePreferences.js 33 bytes 884 bytes 3.88%
modules/feed/hooks/useNotifications.js 33 bytes 695 bytes 4.98%
modules/feed/hooks/useFeedSettings.js 37 bytes 548 bytes 7.24% ⚠️
modules/core/hooks/useKnockAuthState.js (New) 292 bytes 292 bytes 100.0% 🚀

Files in index.js:

  • ./src/index.ts → Total Size: 0 bytes

Files in modules/guide/context/KnockGuideProvider.js:

  • ./src/modules/guide/context/KnockGuideProvider.tsx → Total Size: 1.55kB

Files in modules/core/context/KnockProvider.js:

  • ./src/modules/core/context/KnockProvider.tsx → Total Size: 1.19kB

Files in modules/core/hooks/useAuthenticatedKnockClient.js:

  • ./src/modules/core/hooks/useAuthenticatedKnockClient.ts → Total Size: 1.92kB

Files in modules/core/utils.js:

  • ./src/modules/core/utils.ts → Total Size: 1.48kB

Files in modules/feed/hooks/useFeedSettings.js:

  • ./src/modules/feed/hooks/useFeedSettings.ts → Total Size: 935 bytes

Files in modules/core/hooks/useKnockAuthState.js:

  • ./src/modules/core/hooks/useKnockAuthState.ts → Total Size: 102 bytes
view changes for bundle: @knocklabs/expo-expo-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
modules/push/KnockExpoPushNotificationProvider.mjs 240 bytes 3.6kB 7.13% ⚠️

Files in modules/push/KnockExpoPushNotificationProvider.mjs:

  • ./src/modules/push/KnockExpoPushNotificationProvider.tsx → Total Size: 4.98kB
view changes for bundle: @knocklabs/client-client-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
clients/guide/client.mjs 825 bytes 25.35kB 3.36%
clients/feed/feed.mjs 1.17kB 15.71kB 8.02% ⚠️
knock.mjs 1.84kB 6.56kB 38.88% ⚠️
clients/ms-*.mjs 545 bytes 3.05kB 21.79% ⚠️
clients/slack/index.mjs 367 bytes 2.4kB 18.07% ⚠️
clients/messages/index.mjs 158 bytes 1.99kB 8.62% ⚠️
clients/feed/index.mjs 132 bytes 1.45kB 10.05% ⚠️

Files in clients/guide/client.mjs:

  • ./src/clients/guide/client.ts → Total Size: 30.04kB

Files in clients/feed/feed.mjs:

  • ./src/clients/feed/feed.ts → Total Size: 18.12kB

Files in knock.mjs:

  • ./src/knock.ts → Total Size: 6.97kB

Files in clients/ms-*.mjs:

  • ./src/clients/ms-teams/index.ts → Total Size: 3.17kB

Files in clients/slack/index.mjs:

  • ./src/clients/slack/index.ts → Total Size: 2.37kB

Files in clients/messages/index.mjs:

  • ./src/clients/messages/index.ts → Total Size: 2.0kB

Files in clients/feed/index.mjs:

  • ./src/clients/feed/index.ts → Total Size: 1.2kB

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b9854aa. Configure here.

kylemcd added 2 commits July 9, 2026 15:49
Replace the disposedRef/generation StrictMode self-heal with a plain effect
cleanup that tears the client down on unmount. This removes the one-render
window where a StrictMode simulated remount could return the previous
(torn-down) memoized client before the generation bump rebuilt it.

Addresses Cursor BugBot: "StrictMode serves torn-down client".
Wait for the connection status to resolve back to "connected" (which only
happens after the second authCheck promise resolves) rather than asserting it
synchronously right after waitFor(authCheck called twice), which was flaky
under full-suite timing.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.40%. Comparing base (70e7669) to head (77d1f6c).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/client/src/clients/guide/client.ts 91.66% 7 Missing ⚠️
...modules/push/KnockExpoPushNotificationProvider.tsx 45.45% 6 Missing ⚠️
...e/src/modules/slack/context/KnockSlackProvider.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1033      +/-   ##
==========================================
+ Coverage   64.49%   65.40%   +0.91%     
==========================================
  Files         212      213       +1     
  Lines       10217    10473     +256     
  Branches     1389     1487      +98     
==========================================
+ Hits         6589     6850     +261     
+ Misses       3603     3598       -5     
  Partials       25       25              
Files with missing lines Coverage Δ
packages/client/src/clients/feed/feed.ts 83.45% <100.00%> (+1.95%) ⬆️
packages/client/src/clients/feed/index.ts 100.00% <100.00%> (ø)
packages/client/src/clients/messages/index.ts 100.00% <100.00%> (ø)
packages/client/src/clients/ms-teams/index.ts 100.00% <100.00%> (ø)
packages/client/src/clients/slack/index.ts 100.00% <100.00%> (ø)
packages/client/src/knock.ts 100.00% <100.00%> (ø)
packages/react-core/src/index.ts 100.00% <ø> (ø)
...ct-core/src/modules/core/context/KnockProvider.tsx 96.42% <100.00%> (+0.20%) ⬆️
...ackages/react-core/src/modules/core/hooks/index.ts 100.00% <100.00%> (ø)
.../modules/core/hooks/useAuthenticatedKnockClient.ts 100.00% <100.00%> (ø)
... and 11 more

... and 2 files with indirect coverage changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Add enabled prop to KnockProvider

1 participant