Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[keep-awake] Stop useKeepAwake calls deactivating each other by default. #28884

Merged

Conversation

macksal
Copy link
Contributor

@macksal macksal commented May 15, 2024

Why

Currently, useKeepAwake has an unexpected quirk. If two mounted components call useKeepAwake() without providing an explicit tag, the screen can go to sleep after either one is unmounted - even if the other is present.

This result is surprising and goes against the most obvious interpretation of the useKeepAwake docs:

A React hook to keep the screen awake for as long as the owner component is mounted.

A more sensible default behaviour would be that mounting multiple components that each call the hook would each bind the keep-awake effect to their own lifecycle, and the screen could only be dimmed if all the components were unmounted in turn. However, with the default behaviour, if any one of them unmounts, the effect from all of them is deactivated.

My primary use case is a component that performs a long-running task on a button press, showing a progress bar to the user. I need to keep the screen awake as long as the task is running. Now let's say the user performs two tasks at the same time on two adjacent components. The completion of one will deactivate the keep-awake effect and allow the screen to dim while the other task is still running.

An additional motivation is that developers can be tripped up by expo activating expo-keep-awake by default in development mode. The default tag is used there, which means that any call to deactivateKeepAwake without an explicit tag will deactivate the devtools' keep-awake. This means the default behaviour only lasts until a component calling useKeepAwake() is mounted and then unmounted. This is another unexpected side effect of what otherwise appears to be a declarative API.

How

This happens because the tag passed to the native implementation defaults to a global constant value if none is explicitly provided. The native implementation doesn't count the number of activations for each tag, but instead adds or removes the tag from a set and keeps the screen awake as long as at least one tag is present in the set.

When no tag is provided to useKeepAwake, let's stop using the shared default tag. Instead, we'll generate a tag which is unique to the component calling useKeepAwake. React's useId is a drop-in candidate to create the unique tag per-component.

Other options considered and rejected:

  • Use a different mechanism than useId to create a unique tag for each caller of the hook. A simple random string generated inside the body of the useEffect would be fine. On the other hand, useId is already designed to be unique based on the component hierarchy, and React is already a dependency and isn't going anywhere.
  • Modify the native implementation to use a multimap/counter to store tags instead of a set. This would mean that calls to activateKeepAwakeAsync would need to be matched by an equal number of calls to deactivateKeepAwakeAsync with the same tag. This change would be a lot more pervasive and change the imperative part of the API significantly.

Test Plan

A unit test has been added that checks that two different calls to useKeepAwake each pass a different tag to the activate method in the native layer. It fails in the current SDK and passes with the changes in this PR.

Checklist

@macksal macksal requested a review from brentvatne as a code owner May 15, 2024 04:06
@expo-bot expo-bot added the bot: suggestions ExpoBot has some suggestions label May 15, 2024
macksal added a commit to macksal/expo that referenced this pull request May 15, 2024
@macksal macksal force-pushed the macksal/bugfix/use-keep-awake-unique-tags branch from 713be97 to bc9ecdd Compare May 15, 2024 04:10
@expo-bot expo-bot added bot: passed checks ExpoBot has nothing to complain about and removed bot: suggestions ExpoBot has some suggestions labels May 15, 2024
Copy link
Member

@tsapeta tsapeta left a comment

Choose a reason for hiding this comment

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

Thank you @macksal for this PR and providing a very good explanation and motivation about this change! 🙇‍♂️

It looks good to me 👍 I'm only wondering if it should be a breaking change or a bug fix, considering the fact that the documentation should be the source of truth and expected behavior and the current implementation not doing exactly what is said in the docs. What do you think @macksal @alanjhughes?

Copy link
Collaborator

@alanjhughes alanjhughes left a comment

Choose a reason for hiding this comment

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

Thanks @macksal! I think it's a bug fix rather than a breaking change. I don't think there would be a lot of people relying on the current broken behaviour and breaking dev mode is very unexpected. This will likely go unnoticed by most @tsapeta

@macksal macksal force-pushed the macksal/bugfix/use-keep-awake-unique-tags branch from bc9ecdd to e682fef Compare May 15, 2024 09:31
@macksal
Copy link
Contributor Author

macksal commented May 15, 2024

@alanjhughes @tsapeta I agree - I had it marked as a bugfix but I preempted a complaint about changing the behaviour 😅 I've just moved the changelog entry.

Thanks for approving and feel free to merge as-is.

@tsapeta tsapeta merged commit 5d32712 into expo:main May 15, 2024
3 checks passed
@macksal macksal deleted the macksal/bugfix/use-keep-awake-unique-tags branch May 15, 2024 10:32
@brentvatne brentvatne added the published Changes from the PR have been published to npm label May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot: passed checks ExpoBot has nothing to complain about published Changes from the PR have been published to npm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants