Skip to content

Commit a4581ec

Browse files
huntiefacebook-github-bot
authored andcommitted
Fix/simplify invariant for ColorSchemeName, align manual typedef (#53397)
Summary: Pull Request resolved: #53397 This is a runtime behaviour fix and an API change to `Appearance.setColorScheme`, motivated by a user report where providing `'unspecified'` (valid) to this function would trigger an incorrect invariant throw. Furthermore, there is already a [first party use](https://github.com/facebook/react-native/blob/aec35b896053d9372ccdaf67c939b2eb216d3455/packages/react-native/Libraries/Utilities/Appearance.js#L101) where we call `Appearance.setColorScheme('unspecified')`. **Changes** - `Appearance.d.ts` (current public API, manual types): Fix `ColorSchemeName` type to include `'unspecified'` value, and narrow to remove nullability — aligning with existing Flow source for this type in `NativeAppearance`. - `Appearance.js` (implementation): Fix the invariant throw by **removing it**, and instead narrowing the input type to non-nullable. Redundant work in `getState` and `getColorScheme` is removed. Changelog: [General][Breaking] `Appearance.setColorScheme` no longer accepts a nullable value Reviewed By: andrewdacenko Differential Revision: D80705652 fbshipit-source-id: cf221a33447606653050d471ca2d0347ab30db81
1 parent b7e64be commit a4581ec

File tree

4 files changed

+16
-29
lines changed

4 files changed

+16
-29
lines changed

packages/react-native/Libraries/Utilities/Appearance.d.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import {NativeEventSubscription} from '../EventEmitter/RCTNativeAppEventEmitter';
1111

12-
type ColorSchemeName = 'light' | 'dark' | null | undefined;
12+
type ColorSchemeName = 'light' | 'dark' | 'unspecified';
1313

1414
export namespace Appearance {
1515
type AppearancePreferences = {
@@ -26,17 +26,15 @@ export namespace Appearance {
2626
*
2727
* Example: `const colorScheme = Appearance.getColorScheme();`
2828
*/
29-
export function getColorScheme(): ColorSchemeName;
29+
export function getColorScheme(): ColorSchemeName | null | undefined;
3030

3131
/**
3232
* Set the color scheme preference. This is useful for overriding the default
3333
* color scheme preference for the app. Note that this will not change the
3434
* appearance of the system UI, only the appearance of the app.
3535
* Only available on iOS 13+ and Android 10+.
3636
*/
37-
export function setColorScheme(
38-
scheme: ColorSchemeName | null | undefined,
39-
): void;
37+
export function setColorScheme(scheme: ColorSchemeName): void;
4038

4139
/**
4240
* Add an event handler that is fired when appearance preferences change.

packages/react-native/Libraries/Utilities/Appearance.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import typeof INativeAppearance from './NativeAppearance';
1414

1515
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
1616
import EventEmitter from '../vendor/emitter/EventEmitter';
17-
import invariant from 'invariant';
1817

1918
export type {AppearancePreferences};
2019

@@ -60,7 +59,7 @@ function getState(): $NonMaybeType<typeof lazyState> {
6059
appearanceChanged: [AppearancePreferences],
6160
}>(NativeAppearance).addListener('appearanceChanged', newAppearance => {
6261
state.appearance = {
63-
colorScheme: toColorScheme(newAppearance.colorScheme),
62+
colorScheme: newAppearance.colorScheme,
6463
};
6564
eventEmitter.emit('change', state.appearance);
6665
});
@@ -83,7 +82,7 @@ export function getColorScheme(): ?ColorSchemeName {
8382
// Lazily initialize `state.appearance`. This should only
8483
// happen once because we never reassign a null value to it.
8584
state.appearance = {
86-
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
85+
colorScheme: NativeAppearance.getColorScheme(),
8786
};
8887
}
8988
colorScheme = state.appearance.colorScheme;
@@ -94,13 +93,13 @@ export function getColorScheme(): ?ColorSchemeName {
9493
/**
9594
* Updates the current color scheme to the supplied value.
9695
*/
97-
export function setColorScheme(colorScheme: ?ColorSchemeName): void {
96+
export function setColorScheme(colorScheme: ColorSchemeName): void {
9897
const state = getState();
9998
const {NativeAppearance} = state;
10099
if (NativeAppearance != null) {
101-
NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
100+
NativeAppearance.setColorScheme(colorScheme);
102101
state.appearance = {
103-
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
102+
colorScheme,
104103
};
105104
}
106105
}
@@ -114,14 +113,3 @@ export function addChangeListener(
114113
const {eventEmitter} = getState();
115114
return eventEmitter.addListener('change', listener);
116115
}
117-
118-
/**
119-
* TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union
120-
*/
121-
function toColorScheme(colorScheme: ?string): ?ColorSchemeName {
122-
invariant(
123-
colorScheme === 'dark' || colorScheme === 'light' || colorScheme == null,
124-
"Unrecognized color scheme. Did you mean 'dark', 'light' or null?",
125-
);
126-
return colorScheme;
127-
}

packages/react-native/ReactNativeApi.d.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<b43428758b3aefd30b2bd8a32ec0878a>>
7+
* @generated SignedSource<<0170bc4ced1f6d3d4092d837027cd391>>
88
*
99
* This file was generated by scripts/js-api/build-types/index.js.
1010
*/
@@ -4642,9 +4642,7 @@ declare type Separators = {
46424642
updateProps: (select: "leading" | "trailing", newProps: Object) => void
46434643
}
46444644
declare type sequence = typeof sequence
4645-
declare function setColorScheme(
4646-
colorScheme: ColorSchemeName | null | undefined,
4647-
): void
4645+
declare function setColorScheme(colorScheme: ColorSchemeName): void
46484646
declare function setComponentProviderInstrumentationHook(
46494647
hook: ComponentProviderInstrumentationHook,
46504648
): void
@@ -5972,7 +5970,7 @@ export {
59725970
AppState, // f7097b1b
59735971
AppStateEvent, // 80f034c3
59745972
AppStateStatus, // 447e5ef2
5975-
Appearance, // b23e8105
5973+
Appearance, // 00cbaa0a
59765974
AutoCapitalize, // c0e857a0
59775975
BackHandler, // a9f9bad9
59785976
BackPressEventName, // 4620fb76

packages/rn-tester/js/examples/Appearance/AppearanceExample.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ const ColorShowcase = (props: {themeName: string}) => (
136136

137137
const ToggleNativeAppearance = () => {
138138
const [nativeColorScheme, setNativeColorScheme] =
139-
useState<ColorSchemeName | null>(null);
139+
useState<ColorSchemeName>('unspecified');
140140
const colorScheme = useColorScheme();
141141

142142
useEffect(() => {
@@ -155,7 +155,10 @@ const ToggleNativeAppearance = () => {
155155
title="Set to dark"
156156
onPress={() => setNativeColorScheme('dark')}
157157
/>
158-
<Button title="Unset" onPress={() => setNativeColorScheme(null)} />
158+
<Button
159+
title="Unset"
160+
onPress={() => setNativeColorScheme('unspecified')}
161+
/>
159162
</View>
160163
);
161164
};

0 commit comments

Comments
 (0)