fix(LinkPreview): avoid render loop on persistent errors (release 0.4.3)#11
Merged
Conversation
LinkPreview's success/error effects listed the callback props in their
dependency arrays:
useEffect(() => { if (data) onSuccess?.(data); }, [data, onSuccess]);
useEffect(() => { if (error) onError?.(error); }, [error, onError]);
Any consumer that passed an inline arrow (a fresh function reference
each render) would re-fire the effect every render. For an `error`
that persists, that drives an infinite loop:
effect runs -> onError fires -> consumer setState -> consumer
re-renders -> new onError reference -> effect runs again ...
Fix: read the latest callback through a ref each invocation. The
effects now depend only on `data` / `error`, so they fire exactly when
the underlying state changes — independent of how the consumer wires
up their callback. No new public API; behavior unchanged for stable
(useCallback'd) callbacks. Existing test suite (90/90) still green.
`pnpm install` auto-runs workspace packages' `prepare` scripts. The root `package.json` also had its own `prepare` that delegated back to the same lib via `pnpm --filter`, which fired in parallel with the auto-triggered one. Two `bob build` processes raced — one cleaned `lib/module/` while the other was mid-write — surfacing as ENOENT on .map files in CI. Rename the root convenience script from `prepare` to `build` so it's no longer a lifecycle hook. Workspace install runs the lib's prepare once; CI/release call `pnpm build` explicitly when they want a fresh artifact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a render-loop bug in
<LinkPreview />triggered by callers passing inlineonSuccess/onErrorarrows when the URL produces a persistent error.Repro
When the URL errors and the error state persists, React fires Maximum update depth exceeded — the component pegs the CPU and never settles.
Root cause
The component listed callback props in effect dependency arrays:
An inline arrow gets a new function reference on every render. When the consumer's
onErrorruns and triggers asetState, the consumer re-renders, creating a newonErrorreference, which re-fires the effect, which callsonErroragain, etc. For success the loop fires once per render too — wasteful but not infinite. For a persistent error it loops forever.Fix
Read the latest callback through a ref each invocation. Effects now depend only on
data/error, so they fire exactly when the underlying state changes — independent of how the consumer wires up their callback.No public API change. Behavior is unchanged for stable (
useCallback'd) callbacks, and now also correct for inline arrows.Verification
pnpm prepare(bob build) clean.Release
Version bumped from
0.4.2→0.4.3. On merge tomain,release.ymltriggers and publishesreact-native-preview-url@0.4.3to npm with provenance.Test plan
mainand confirm0.4.3lands on npmNote
The same fix is already on
feat/docs(commitd4e3a19) — once this PR merges, that branch can be rebased onmainand the duplicate commit will deduplicate via patch-id.