YPE-1565: Show error when YouVersionProvider has no appKey#264
YPE-1565: Show error when YouVersionProvider has no appKey#264cameronapak wants to merge 6 commits into
Conversation
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 detectedLatest commit: 41e3473 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
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>
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Okay, so does the localization repo need a PR and that PR merged in before I can work on this? @camrun91
There was a problem hiding this comment.
I think this got handled correct?
- 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>
|
@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 |
| 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); |
There was a problem hiding this 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.
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.
Empty/missing
appKeyrendered a blank page. Now: UI provider shows a styled "Missing app key" message; hooks provider throws.MissingAppKeypanel + Storybook storyYPE-1565
🤖 Generated with Claude Code
Greptile Summary
This PR surfaces a clear developer error when
YouVersionProviderreceives a missing or emptyappKey, replacing the previous silent blank-page failure. The UI package now renders a styledMissingAppKeypanel (with i18n support in en/fr/es), while the hooks-only package throws a descriptiveError.packages/ui): Guards on!appKey?.trim()before delegating to the base provider, rendersMissingAppKeywith correct theme resolution and logs toconsole.errorfor developer guidance; includes unit tests, Storybook stories, and the example app updated to remove the empty-string default.packages/hooks): Throws a descriptive error before executing any context setup, with parametrized unit tests coveringundefined, empty string, and whitespace inputs.minor, correctly reflecting the new user-visible behaviour.Confidence Score: 4/5
Safe to merge after the open review thread on the hooks provider is resolved; the UI provider path is correct and well-tested.
The hooks provider places its guard throw before the first hook call (
useResolvedTheme). If a component ever mounts with a valid key (hooks run) and is then re-rendered with an empty key (throw fires before hooks), React will detect the inconsistent hook count. This was flagged in the previous review thread and remains unaddressed in the current diff, making the hooks provider path unreliable under dynamic appKey changes.packages/hooks/src/context/YouVersionProvider.tsx — the ordering of the early-exit guard relative to hook invocations needs to be resolved before merging.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[YouVersionProvider rendered] --> B{appKey empty or missing?} B -- Yes, UI package --> C[console.error to developer] C --> D[Render YvStyles + MissingAppKey panel] B -- Yes, hooks-only package --> E[throw Error] E --> F[Nearest React Error Boundary] B -- No --> G[Render BaseYouVersionProvider] G --> H[Children rendered normally]%%{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 rendered] --> B{appKey empty or missing?} B -- Yes, UI package --> C[console.error to developer] C --> D[Render YvStyles + MissingAppKey panel] B -- Yes, hooks-only package --> E[throw Error] E --> F[Nearest React Error Boundary] B -- No --> G[Render BaseYouVersionProvider] G --> H[Children rendered normally]Reviews (5): Last reviewed commit: "Merge branch 'main' into YPE-1565-missin..." | Re-trigger Greptile