Skip to content

Add useCodePush() - #111

Merged
ofalvai merged 1 commit into
masterfrom
add-use-code-push
Nov 11, 2026
Merged

ofalvai merged 1 commit into
masterfrom
add-use-code-push

Conversation

@ofalvai

@ofalvai ofalvai commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Background

#107 added <CodePushProvider>, which runs the update checks for the app. Components still can't show what CodePush is doing: whether a check is running, whether an update is waiting, or why the last check failed. Today an app has to pass callbacks to sync() and keep that state itself.

Change

Adds a new React hook, useCodePush(). It returns the update state and the functions that change it, and re-renders the component when the state changes. It works inside <CodePushProvider> (and throws outside it, because without the Provider nothing calls notifyAppReady() and every update rolls back).

function CheckForUpdatesButton() {
  const { isChecking, checkError, sync } = useCodePush();

  return (
    <View>
      <Button
        title={isChecking ? "Checking for updates..." : "Check for updates"}
        disabled={isChecking}
        onPress={() => sync({ updateDialog: true }).catch(() => {})}
      />
      {checkError && <Text>Could not check for updates.</Text>}
    </View>
  );
}

The state has isChecking, isUpdateAvailable, availableUpdate, incompatibleUpdate and checkError. incompatibleUpdate is set when the server has a release that this binary can't install because of the update's target version constraint. Until now only a callback reported this.

checkForUpdate() in CodePush.js reports each check to a small store. sync() and the codePush() HOC check through checkForUpdate(), so every check shows in the state: the Provider's, the hook's, and direct calls to the old functions. The hook reads the store with useSyncExternalStore.

flowchart LR
  Provider[CodePushProvider] --> sync
  HOC["codePush() HOC"] --> sync
  App[app code] --> sync
  App --> check[checkForUpdate]
  sync --> check
  check -- check events --> store[update store]
  store -- useSyncExternalStore --> hook["useCodePush()"]
Loading

Decisions

  • Single reporting point in the legacy code: The first iteration of this change left CodePush.js alone and implemented everything in /src (wrapped sync() in the hook, guessing the check's progress from status callbacks). It came out more than twice as large, missed direct calls and the HOC, never knew which update sync() found, and needed a "this call doesn't update the hook's state" warning all over the docs. In this version, the store lives at module level, outside React, because the old functions must keep updating it.
  • The hook's sync(options) and checkForUpdate({ deploymentKey }) take no callbacks, unlike the existing codePush.sync() and codePush.checkForUpdate() methods: This is a new public API, so it is the time to define a modern and intuitive API. Sync status and download progress should become state instead (next PR).
  • isUpdateAvailable is false for an update that failed to install before, because sync() skips those by default (existing behavior). It isn't exact when rollbackRetryOptions allows a retry, but close enough.
  • incompatibleUpdate.targetBinaryVersion is an optional field: We plan an automatic compatibility check (like Expo's fingerprints), which won't require setting a "target" binary version at all.

Out of scope

  • Sync status and download progress in the hook's state. That's the next PR.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings November 6, 2026 09:58

Copilot AI 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.

🟢 Approval recommended

The review reports no active issues with the new hook and shared update-state integration.

No open issues 🎉

What changed in this PR

Adds useCodePush() so components inside CodePushProvider can display update-check state and trigger update actions. It builds on the Provider introduced in #107 while also reflecting checks made through legacy APIs.

Changes:

  • Adds a Provider-backed hook exposing check state, incompatible releases, errors, and update actions.
  • Reports checks from CodePush.js to a shared, React-independent store.
  • Adds public types, usage documentation, and unit tests for state transitions and subscriptions.
File Description
typings/​react-native-code-push.d.ts Declares the hook, state, and action types.
src/​update-state/​update-store.ts Adds the observable update-state store.
src/​update-state/​update-state.ts Handles check results, failures, and overlapping checks.
src/​update-state/​__tests__/​update-store.test.ts Tests subscriptions, snapshot identity, and store isolation.
src/​update-state/​__tests__/​update-state.test.ts Tests state transitions and overlapping checks.
src/​react/​use-code-push.ts Adds the hook with store subscriptions and a Provider guard.
src/​react/​store-context.ts Defines context for the store and update actions.
src/​react/​code-push-provider.tsx Supplies store context to child components.
docs/​api-js.md Documents hook behavior and usage.
CodePush.js Reports checks to the store and exports the wired hook.

Review effort: Balanced

@ofalvai
ofalvai merged commit 8de59f7 into master Nov 11, 2026
6 checks passed
@ofalvai
ofalvai deleted the add-use-code-push branch November 11, 2026 09:56
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.

3 participants