Skip to content

YPE-1565: Show error when YouVersionProvider has no appKey#264

Open
cameronapak wants to merge 7 commits into
mainfrom
YPE-1565-missing-app-key-error
Open

YPE-1565: Show error when YouVersionProvider has no appKey#264
cameronapak wants to merge 7 commits into
mainfrom
YPE-1565-missing-app-key-error

Conversation

@cameronapak

@cameronapak cameronapak commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator
image

Empty/missing appKey rendered a blank page. Now: UI provider shows a styled "Missing app key" message; hooks provider throws.

  • Styled MissingAppKey panel + Storybook story
  • i18n (en/fr/es), example cleanup, unit + integration tests, changeset

YPE-1565

🤖 Generated with Claude Code

Greptile Summary

This PR surfaces a clear, styled error when YouVersionProvider receives a missing or empty appKey, replacing the previous blank-page behaviour. The UI package renders a MissingAppKey panel and the hooks package throws a descriptive error.

  • UI package: Early guard in YouVersionProvider renders a styled MissingAppKey panel with i18n text and writes a developer-readable message to console.error; base provider is never invoked with an invalid key.
  • Hooks package: Unconditional throw before hook calls when appKey is missing, giving hooks-only consumers a clear error (note: this placement raises a Rules-of-Hooks concern already flagged in a previous review thread).
  • Both paths are covered by unit tests and the example app is updated to propagate the new behaviour instead of silently defaulting to an empty string.

Confidence Score: 5/5

Safe to merge once the Rules-of-Hooks concern in the hooks provider (raised in the previous review thread) is resolved; the UI-package changes are well-structured and tested.

The UI and hooks providers each add a focused, well-tested guard for an invalid appKey. Tests cover undefined, empty-string, and whitespace inputs for both packages. The only open concern — the throw placed before hook calls in the hooks provider — was already flagged in a previous review thread and is not a new finding in this pass. The remaining observations here are non-blocking style notes.

packages/hooks/src/context/YouVersionProvider.tsx — the throw-before-hooks placement warrants a follow-up; packages/ui/src/components/YouVersionProvider.tsx — console.error in the render body.

Important Files Changed

Filename Overview
packages/hooks/src/context/YouVersionProvider.tsx Adds an early throw guard when appKey is missing; placed before all hook calls (already flagged in a previous review thread as a Rules of Hooks violation when the component transitions between valid and invalid appKey states)
packages/ui/src/components/YouVersionProvider.tsx Guards against missing appKey and renders MissingAppKey; hook order is correct (useEffect fires before the guard), but console.error is called in the render body rather than a useEffect
packages/ui/src/components/missing-app-key.tsx New styled error panel with i18n, accessible role="alert", and light/dark theme support; aria-live conflict already flagged in a previous review thread
packages/ui/src/components/YouVersionProvider.test.tsx Adds parameterised tests for undefined, empty-string, and whitespace-only appKey values; verifies alert role, error text, child suppression, and console.error call
packages/hooks/src/context/YouVersionProvider.test.tsx Adds parameterised throw tests for undefined, empty, and whitespace appKey; good coverage of the new guard
packages/ui/src/components/missing-app-key.stories.tsx Light, Dark, and integration play-function stories added; well-structured with autodocs and inline-radio theme control
examples/vite-react/src/ThemedApp.tsx Removes the empty-string default for VITE_YVP_APP_KEY so an unset env var now propagates to the provider and triggers the MissingAppKey panel; intentional and well-commented
.changeset/missing-app-key-message.md Correctly bumps both platform-react-hooks and platform-react-ui as minor releases with a clear description

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[YouVersionProvider receives props] --> B{appKey missing or empty?}
    
    B -- Yes, UI package --> C[console.error developer message]
    C --> D[Render YvStyles + MissingAppKey panel]
    D --> E[resolveTheme static snapshot for panel theme]
    
    B -- Yes, hooks package --> F[throw Error]
    F --> G[Nearest error boundary catches]
    
    B -- No, both packages --> H[UI: pass through to BaseYouVersionProvider]
    H --> I[Hooks: call useResolvedTheme, useMemo, useEffect]
    I --> J[Render children with context]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[YouVersionProvider receives props] --> B{appKey missing or empty?}
    
    B -- Yes, UI package --> C[console.error developer message]
    C --> D[Render YvStyles + MissingAppKey panel]
    D --> E[resolveTheme static snapshot for panel theme]
    
    B -- Yes, hooks package --> F[throw Error]
    F --> G[Nearest error boundary catches]
    
    B -- No, both packages --> H[UI: pass through to BaseYouVersionProvider]
    H --> I[Hooks: call useResolvedTheme, useMemo, useEffect]
    I --> J[Render children with context]
Loading

Reviews (6): Last reviewed commit: "Merge branch 'main' into YPE-1565-missin..." | Re-trigger Greptile

Render a styled "Missing app key" message instead of a blank page; hooks
provider throws for hooks-only consumers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8773650

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

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-react-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch
@youversion/platform-core Minor

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

Comment thread packages/ui/src/components/missing-app-key.tsx Outdated
role="alert" already implies aria-live="assertive"; the explicit
polite value downgraded announcement urgency for a config error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hey @camrun91, what do we do about locales that we add that aren't yet Crowdin translated? Do we put in potential strings or leave those in English in these locale files?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What do you mean new locals? If you are talking adding new strings the process will be to add them to the file in the localization repo. This is a good test to find out the best way to do this as the english source files shouldn't need to be translated and we need a process to make this quicker

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Okay, so does the localization repo need a PR and that PR merged in before I can work on this? @camrun91

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this got handled correct?

cameronapak and others added 2 commits June 24, 2026 12:46
- Remove redundant `missingAppKey*` translation keys
- Reuse generic `errorHeading` and `invalidAppKeyError` strings
- Add a `console.error` in `YouVersionProvider` for developers
The previous commit reused errorHeading/invalidAppKeyError and moved the
actionable developer guidance to console.error. Update the assertions to
match: expect "Error" instead of "Missing app key", and verify the
console.error guidance fires. Also refresh the guard comment to reflect
that the panel is intentionally generic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@Dustin-Kelley can you review this? I'm no longer adding new strings to this and re-using old ones

P.S. I'll learn the new strings process and share that with you soon

Comment on lines +87 to 94
if (!appKey?.trim()) {
throw new Error(
'YouVersionProvider: a non-empty "appKey" is required. If you load it from an ' +
'environment variable, make sure it is set and restart your dev server.',
);
}

const resolvedTheme = useResolvedTheme(theme);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Conditional throw placed before hook calls violates Rules of Hooks

The throw guard on line 87 exits the function before useResolvedTheme, useMemo, and useEffect are called (lines 94–116). React requires hooks to be called in the same order on every render. If a component ever receives a valid appKey (hooks run), then is re-rendered with an empty/undefined one (hooks are skipped because the throw fires first), React will detect an inconsistent hook count and either log a rules-of-hooks error or behave unpredictably. The react-hooks/rules-of-hooks ESLint rule flags exactly this pattern.

The guard should be moved to run after all hook invocations, or the component should be split so the hook-bearing path is never entered when appKey is invalid.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/hooks/src/context/YouVersionProvider.tsx
Line: 87-94

Comment:
**Conditional throw placed before hook calls violates Rules of Hooks**

The `throw` guard on line 87 exits the function before `useResolvedTheme`, `useMemo`, and `useEffect` are called (lines 94–116). React requires hooks to be called in the same order on every render. If a component ever receives a valid `appKey` (hooks run), then is re-rendered with an empty/undefined one (hooks are skipped because the throw fires first), React will detect an inconsistent hook count and either log a rules-of-hooks error or behave unpredictably. The `react-hooks/rules-of-hooks` ESLint rule flags exactly this pattern.

The guard should be moved to run after all hook invocations, or the component should be split so the hook-bearing path is never entered when `appKey` is invalid.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex

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.

2 participants