Skip to content

The exported update-only hook invokes its effect during initial mount #417

Description

@MicroMilo

Summary

The exported useUpdateEffect hook contradicts its update-only contract by invoking the callback during its first post-mount effect flush.

Validation source commit: 0337840e9ec0f26840252a658c1a6bf8c1b6dbf1.

Code path

  • src/hooks/useUpdateEffect.ts:3-7: JSDoc lifecycle claim: the hook runs only on updates, not initial mount.
  • src/hooks/useUpdateEffect.ts:8-16: Implementation falls through the initial-mount branch and invokes effect().
  • src/hooks/index.ts:4-4: Public barrel export for the documented hook.
  • Repository-local validation artifact: Minimized executable mount/flush witness.

Steps to reproduce

This report is based on a source-control-flow validation against the commit above.

  1. Check out the source commit listed in this report.
  2. Inspect the code path and contract anchors listed above.
  3. Exercise the described boundary/state path: The exported update-only hook invokes its effect during initial mount.
  4. Compare the observed behavior with the expected contract below.

Validation note: The validation imports the frozen hook and supplies only deterministic React mount primitives. Sequence: initial render -> post-mount effect flush. Expected callback count: 0. Observed: 1. Output: {"sequence":["initial render","post-mount effect flush"],"expectedCallbackCount":0,"actualCallbackCount":1,"violation":true}.

Expected behavior

The implementation should preserve the repository contract described by the documentation, public type, parser, serializer, or sibling implementation anchors above.

Actual behavior

The exported useUpdateEffect hook contradicts its update-only contract by invoking the callback during its first post-mount effect flush.

Existing coverage

I did not find an existing issue or PR title that appears to cover this exact root cause in the pre-submission check.

Suggested fix

Suppress useUpdateEffect's callback during the initial post-mount effect flush.

Suggested tests

  • Add a regression test for: The exported update-only hook invokes its effect during initial mount.
  • Include the boundary value or state transition described above so the old behavior fails before the fix.

Submitted with Codex.

Activity

  1. lyjx29 commented on Aug 7, 2026

    @lyjx29
    Contributor

    Hi! I'd like to fix this one too. I confirmed the bug: on the initial mount the hook flips isInitialMount to false but still runs effect(), so the update-only contract is violated. I've fixed it to return early on the first mount (so no effect runs) and added unit tests covering: no effect on mount, effect on updates only, and cleanup ordering between updates. Opening a PR shortly. Thanks!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions