mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Expose static methods to manipulate the StatusBar stack imperatively (#21206)
Summary: This PR exposes three static methods (`pushStackEntry`, `popStackEntry`, and `replaceStackEntry`) on StatusBar that enable imperative manipulation of the StatusBar style within the stack established by mounted StatusBar components. Motivation: ---------- The StatusBar **component** provides a sensible API for manipulating that StatusBar style: every time a StatusBar component is mounted, its props are pushed onto a stack, and the props from the most recently mounted component are applied. However, there are some scenarios where you may need to manipulate the StatusBar style from imperative code — particularly when invoking imperative third-party APIs that cause UI to appear. (For example, a user feedback utility or bug reporter that launches a full-screen modal.) In modern iOS development, `UIViewControllerBasedStatusBarAppearance` is typically set to `YES`, which allows the third-party UIViewController to specify its preferred status bar style. However, as has been discussed at length in #11710, React Native has disabled this setting, which means that either the app's code or the third-party's React Native wrapper needs to manually manipulate React Native's StatusBar API to achieve the desired outcome. The existing imperative StatusBar APIs are not a good fit for these needs because they simply overwrite the existing StatusBar styles, and provide no means of reverting StatusBar style changes when the third-party UI is dismissed. To improve upon this situation, this PR makes it possible to call `StatusBar.pushStackEntry` before launching the third-party UI, wait for the UI to dismiss, and then call `StatusBar.popStackEntry` (supplying the token returned from the push call). I've featured the new stack-based imperative methods in the documentation, but stopped short of explicitly deprecating the older imperative methods — though I can think of no reason not to deprecate them. Feedback is welcome on this point. Release Notes: -------------- [GENERAL] [ENHANCEMENT] [StatusBar] - Add static methods to manipulate StatusBar stack imperatively Pull Request resolved: https://github.com/facebook/react-native/pull/21206 Differential Revision: D9945247 Pulled By: cpojer fbshipit-source-id: ec118268cff5b47e87be81d0b9e1728ecc3a9b02
This commit is contained in:
committed by
Facebook Github Bot
parent
638d672abc
commit
01f1780314
@@ -182,11 +182,34 @@ function createStackEntry(props: any): any {
|
||||
*
|
||||
* ### Imperative API
|
||||
*
|
||||
* For cases where using a component is not ideal, there is also an imperative
|
||||
* API exposed as static functions on the component. It is however not recommended
|
||||
* to use the static API and the component for the same prop because any value
|
||||
* set by the static API will get overriden by the one set by the component in
|
||||
* the next render.
|
||||
* For cases where using a component is not ideal, there are static methods
|
||||
* to manipulate the `StatusBar` display stack. These methods have the same
|
||||
* behavior as mounting and unmounting a `StatusBar` component.
|
||||
*
|
||||
* For example, you can call `StatusBar.pushStackEntry` to update the status bar
|
||||
* before launching a third-party native UI component, and then call
|
||||
* `StatusBar.popStackEntry` when completed.
|
||||
*
|
||||
* ```
|
||||
* const openThirdPartyBugReporter = async () => {
|
||||
* // The bug reporter has a dark background, so we push a new status bar style.
|
||||
* const stackEntry = StatusBar.pushStackEntry({barStyle: 'light-content'});
|
||||
*
|
||||
* // `open` returns a promise that resolves when the UI is dismissed.
|
||||
* await BugReporter.open();
|
||||
*
|
||||
* // Don't forget to call `popStackEntry` when you're done.
|
||||
* StatusBar.popStackEntry(stackEntry);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* There is a legacy imperative API that enables you to manually update the
|
||||
* status bar styles. However, the legacy API does not update the internal
|
||||
* `StatusBar` display stack, which means that any changes will be overridden
|
||||
* whenever a `StatusBar` component is mounted or unmounted.
|
||||
*
|
||||
* It is strongly advised that you use `pushStackEntry`, `popStackEntry`, or
|
||||
* `replaceStackEntry` instead of the static methods beginning with `set`.
|
||||
*
|
||||
* ### Constants
|
||||
*
|
||||
@@ -300,6 +323,48 @@ class StatusBar extends React.Component<Props> {
|
||||
StatusBarManager.setTranslucent(translucent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a StatusBar entry onto the stack.
|
||||
* The return value should be passed to `popStackEntry` when complete.
|
||||
*
|
||||
* @param props Object containing the StatusBar props to use in the stack entry.
|
||||
*/
|
||||
static pushStackEntry(props: any) {
|
||||
const entry = createStackEntry(props);
|
||||
StatusBar._propsStack.push(entry);
|
||||
StatusBar._updatePropsStack();
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a StatusBar entry from the stack.
|
||||
*
|
||||
* @param entry Entry returned from `pushStackEntry`.
|
||||
*/
|
||||
static popStackEntry(entry: any) {
|
||||
const index = StatusBar._propsStack.indexOf(entry);
|
||||
if (index !== -1) {
|
||||
StatusBar._propsStack.splice(index, 1);
|
||||
}
|
||||
StatusBar._updatePropsStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an existing StatusBar stack entry with new props.
|
||||
*
|
||||
* @param entry Entry returned from `pushStackEntry` to replace.
|
||||
* @param props Object containing the StatusBar props to use in the replacement stack entry.
|
||||
*/
|
||||
static replaceStackEntry(entry: any, props: any) {
|
||||
const newEntry = createStackEntry(props);
|
||||
const index = StatusBar._propsStack.indexOf(entry);
|
||||
if (index !== -1) {
|
||||
StatusBar._propsStack[index] = newEntry;
|
||||
}
|
||||
StatusBar._updatePropsStack();
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
animated: false,
|
||||
showHideTransition: 'fade',
|
||||
@@ -311,33 +376,27 @@ class StatusBar extends React.Component<Props> {
|
||||
// Every time a StatusBar component is mounted, we push it's prop to a stack
|
||||
// and always update the native status bar with the props from the top of then
|
||||
// stack. This allows having multiple StatusBar components and the one that is
|
||||
// added last or is deeper in the view hierarchy will have priority.
|
||||
this._stackEntry = createStackEntry(this.props);
|
||||
StatusBar._propsStack.push(this._stackEntry);
|
||||
this._updatePropsStack();
|
||||
// added last or is deeper in the view hierachy will have priority.
|
||||
this._stackEntry = StatusBar.pushStackEntry(this.props);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// When a StatusBar is unmounted, remove itself from the stack and update
|
||||
// the native bar with the next props.
|
||||
const index = StatusBar._propsStack.indexOf(this._stackEntry);
|
||||
StatusBar._propsStack.splice(index, 1);
|
||||
|
||||
this._updatePropsStack();
|
||||
StatusBar.popStackEntry(this._stackEntry);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const index = StatusBar._propsStack.indexOf(this._stackEntry);
|
||||
this._stackEntry = createStackEntry(this.props);
|
||||
StatusBar._propsStack[index] = this._stackEntry;
|
||||
|
||||
this._updatePropsStack();
|
||||
this._stackEntry = StatusBar.replaceStackEntry(
|
||||
this._stackEntry,
|
||||
this.props,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the native status bar with the props from the stack.
|
||||
*/
|
||||
_updatePropsStack = () => {
|
||||
static _updatePropsStack = () => {
|
||||
// Send the update to the native module only once at the end of the frame.
|
||||
clearImmediate(StatusBar._updateImmediate);
|
||||
StatusBar._updateImmediate = setImmediate(() => {
|
||||
@@ -355,7 +414,7 @@ class StatusBar extends React.Component<Props> {
|
||||
) {
|
||||
StatusBarManager.setStyle(
|
||||
mergedProps.barStyle.value,
|
||||
mergedProps.barStyle.animated,
|
||||
mergedProps.barStyle.animated || false,
|
||||
);
|
||||
}
|
||||
if (!oldProps || oldProps.hidden.value !== mergedProps.hidden.value) {
|
||||
|
||||
Reference in New Issue
Block a user