mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Appearance: Dedupe colorScheme Validation Logic (#46120)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46120 Currently, the implementation of `Appearance` duplicates the validation logic of string `colorScheme` values multiple times. This leads to more complicated code and also unnecessary work in certain edge cases (e.g. when `NativeAppearance` is not registered). This refactors `Appearance` to be simpler and to do less work. I've also configured `NativeAppearance.setColorScheme` to be non-nullable because it has existed since 2023. Changelog: [Internal] Reviewed By: TheSavior Differential Revision: D61567881 fbshipit-source-id: 61cb51709dc716ad97ae1397105414e74fe57a28
This commit is contained in:
committed by
Facebook GitHub Bot
parent
693a575143
commit
ed3ca0730c
+22
-37
@@ -19,9 +19,8 @@ import NativeAppearance, {
|
||||
} from './NativeAppearance';
|
||||
import invariant from 'invariant';
|
||||
|
||||
type AppearanceListener = (preferences: AppearancePreferences) => void;
|
||||
const eventEmitter = new EventEmitter<{
|
||||
change: [AppearancePreferences],
|
||||
change: [{colorScheme: ?ColorSchemeName}],
|
||||
}>();
|
||||
|
||||
type NativeAppearanceEventDefinitions = {
|
||||
@@ -32,24 +31,15 @@ if (NativeAppearance != null) {
|
||||
new NativeEventEmitter<NativeAppearanceEventDefinitions>(
|
||||
NativeAppearance,
|
||||
).addListener('appearanceChanged', (newAppearance: AppearancePreferences) => {
|
||||
const {colorScheme} = newAppearance;
|
||||
invariant(
|
||||
colorScheme === 'dark' || colorScheme === 'light' || colorScheme == null,
|
||||
"Unrecognized color scheme. Did you mean 'dark' or 'light'?",
|
||||
);
|
||||
const colorScheme = toColorScheme(newAppearance.colorScheme);
|
||||
eventEmitter.emit('change', {colorScheme});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Although color scheme is available immediately, it may change at any
|
||||
* time. Any rendering logic or styles that depend on this should try to call
|
||||
* this function on every render, rather than caching the value (for example,
|
||||
* using inline styles rather than setting a value in a `StyleSheet`).
|
||||
*
|
||||
* Example: `const colorScheme = Appearance.getColorScheme();`
|
||||
*
|
||||
* @returns {?ColorSchemeName} Value for the color scheme preference.
|
||||
* Returns the current color scheme preference. This value may change, so the
|
||||
* value should not be cached without either listening to changes or using
|
||||
* the `useColorScheme` hook.
|
||||
*/
|
||||
export function getColorScheme(): ?ColorSchemeName {
|
||||
if (__DEV__) {
|
||||
@@ -59,37 +49,32 @@ export function getColorScheme(): ?ColorSchemeName {
|
||||
return 'light';
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union
|
||||
const nativeColorScheme: ?string =
|
||||
NativeAppearance == null ? null : NativeAppearance.getColorScheme() || null;
|
||||
invariant(
|
||||
nativeColorScheme === 'dark' ||
|
||||
nativeColorScheme === 'light' ||
|
||||
nativeColorScheme == null,
|
||||
"Unrecognized color scheme. Did you mean 'dark' or 'light'?",
|
||||
);
|
||||
return nativeColorScheme;
|
||||
return toColorScheme(NativeAppearance?.getColorScheme());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current color scheme to the supplied value.
|
||||
*/
|
||||
export function setColorScheme(colorScheme: ?ColorSchemeName): void {
|
||||
const nativeColorScheme = colorScheme == null ? 'unspecified' : colorScheme;
|
||||
|
||||
invariant(
|
||||
colorScheme === 'dark' || colorScheme === 'light' || colorScheme == null,
|
||||
"Unrecognized color scheme. Did you mean 'dark', 'light' or null?",
|
||||
);
|
||||
|
||||
if (NativeAppearance != null && NativeAppearance.setColorScheme != null) {
|
||||
NativeAppearance.setColorScheme(nativeColorScheme);
|
||||
}
|
||||
NativeAppearance?.setColorScheme(toColorScheme(colorScheme) ?? 'unspecified');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler that is fired when appearance preferences change.
|
||||
*/
|
||||
export function addChangeListener(
|
||||
listener: AppearanceListener,
|
||||
listener: ({colorScheme: ?ColorSchemeName}) => void,
|
||||
): EventSubscription {
|
||||
return eventEmitter.addListener('change', listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union
|
||||
*/
|
||||
function toColorScheme(colorScheme: ?string): ?ColorSchemeName {
|
||||
invariant(
|
||||
colorScheme === 'dark' || colorScheme === 'light' || colorScheme == null,
|
||||
"Unrecognized color scheme. Did you mean 'dark', 'light' or null?",
|
||||
);
|
||||
return colorScheme;
|
||||
}
|
||||
|
||||
@@ -9155,11 +9155,10 @@ declare export default typeof UTFSequence;
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Utilities/Appearance.js 1`] = `
|
||||
"type AppearanceListener = (preferences: AppearancePreferences) => void;
|
||||
declare export function getColorScheme(): ?ColorSchemeName;
|
||||
"declare export function getColorScheme(): ?ColorSchemeName;
|
||||
declare export function setColorScheme(colorScheme: ?ColorSchemeName): void;
|
||||
declare export function addChangeListener(
|
||||
listener: AppearanceListener
|
||||
listener: ({ colorScheme: ?ColorSchemeName }) => void
|
||||
): EventSubscription;
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -14,19 +14,19 @@ import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboMod
|
||||
|
||||
export type ColorSchemeName = 'light' | 'dark';
|
||||
|
||||
export type AppearancePreferences = {|
|
||||
export type AppearancePreferences = {
|
||||
// TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union
|
||||
// types.
|
||||
/* 'light' | 'dark' */
|
||||
colorScheme?: ?string,
|
||||
|};
|
||||
};
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
// TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union
|
||||
// types.
|
||||
/* 'light' | 'dark' */
|
||||
+getColorScheme: () => ?string;
|
||||
+setColorScheme?: (colorScheme: string) => void;
|
||||
+setColorScheme: (colorScheme: string) => void;
|
||||
|
||||
// RCTEventEmitter
|
||||
+addListener: (eventName: string) => void;
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {
|
||||
AppearancePreferences,
|
||||
ColorSchemeName,
|
||||
} from 'react-native/Libraries/Utilities/NativeAppearance';
|
||||
import type {ColorSchemeName} from 'react-native/Libraries/Utilities/NativeAppearance';
|
||||
|
||||
import {RNTesterThemeContext, themes} from '../../components/RNTesterTheme';
|
||||
import * as React from 'react';
|
||||
@@ -19,20 +16,18 @@ import {useEffect, useState} from 'react';
|
||||
import {Appearance, Button, Text, View, useColorScheme} from 'react-native';
|
||||
|
||||
function ColorSchemeSubscription() {
|
||||
const [colorScheme, setScheme] = useState<?ColorSchemeName | string>(
|
||||
const [colorScheme, setColorScheme] = useState<?ColorSchemeName | string>(
|
||||
Appearance.getColorScheme(),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = Appearance.addChangeListener(
|
||||
(preferences: AppearancePreferences) => {
|
||||
const {colorScheme: scheme} = preferences;
|
||||
setScheme(scheme);
|
||||
({colorScheme: newColorScheme}: {colorScheme: ?ColorSchemeName}) => {
|
||||
setColorScheme(newColorScheme);
|
||||
},
|
||||
);
|
||||
|
||||
return () => subscription?.remove();
|
||||
}, [setScheme]);
|
||||
return () => subscription.remove();
|
||||
}, [setColorScheme]);
|
||||
|
||||
return (
|
||||
<RNTesterThemeContext.Consumer>
|
||||
|
||||
Reference in New Issue
Block a user