Skip to content

Commit 89af21d

Browse files
authored
Merge pull request #295 from mrzachnugent/@zach/277-device-color-scheme
fix(#277): remove async local storage and Splashscreen prevent auto hide
2 parents f072c3d + c04fa6c commit 89af21d

File tree

11 files changed

+2280
-2306
lines changed

11 files changed

+2280
-2306
lines changed

apps/docs/src/content/docs/getting-started/initial-setup.mdx

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ Follow the installation guide for NativeWind from the <a target="_blank" href="
166166
**Platforms:** Web, iOS, and Android
167167

168168
```bash
169-
npx expo install tailwindcss-animate @react-native-async-storage/async-storage class-variance-authority clsx tailwind-merge
169+
npx expo install tailwindcss-animate class-variance-authority clsx tailwind-merge
170170
```
171171
</TabItem>
172172
<TabItem label="Native only">
173173
**Platforms:** iOS and Android
174174

175175
```bash
176-
npx expo install @react-native-async-storage/async-storage class-variance-authority clsx tailwind-merge
176+
npx expo install class-variance-authority clsx tailwind-merge
177177
```
178178
</TabItem>
179179
</Tabs>
@@ -361,9 +361,8 @@ and store the selected theme in the async storage.
361361
code={`
362362
import '~/global.css';
363363
364-
import AsyncStorage from '@react-native-async-storage/async-storage';
365364
import { Theme, ThemeProvider } from '@react-navigation/native';
366-
import { SplashScreen, Stack } from 'expo-router';
365+
import { Stack } from 'expo-router';
367366
import { StatusBar } from 'expo-status-bar';
368367
import * as React from 'react';
369368
import { Platform } from 'react-native';
@@ -384,36 +383,22 @@ export {
384383
ErrorBoundary,
385384
} from 'expo-router';
386385
387-
// Prevent the splash screen from auto-hiding before getting the color scheme.
388-
SplashScreen.preventAutoHideAsync();
389-
390386
export default function RootLayout() {
391-
const { colorScheme, setColorScheme, isDarkColorScheme } = useColorScheme();
387+
const hasMounted = React.useRef(false);
388+
const { colorScheme, isDarkColorScheme } = useColorScheme();
392389
const [isColorSchemeLoaded, setIsColorSchemeLoaded] = React.useState(false);
393390
394-
React.useEffect(() => {
395-
(async () => {
396-
const theme = await AsyncStorage.getItem('theme');
397-
if (Platform.OS === 'web') {
398-
// Adds the background color to the html element to prevent white background on overscroll.
399-
document.documentElement.classList.add('bg-background');
400-
}
401-
if (!theme) {
402-
AsyncStorage.setItem('theme', colorScheme);
403-
setIsColorSchemeLoaded(true);
404-
return;
405-
}
406-
const colorTheme = theme === 'dark' ? 'dark' : 'light';
407-
if (colorTheme !== colorScheme) {
408-
setColorScheme(colorTheme);
409-
410-
setIsColorSchemeLoaded(true);
411-
return;
412-
}
413-
setIsColorSchemeLoaded(true);
414-
})().finally(() => {
415-
SplashScreen.hideAsync();
416-
});
391+
useIsomorphicLayoutEffect(() => {
392+
if (hasMounted.current) {
393+
return;
394+
}
395+
396+
if (Platform.OS === 'web') {
397+
// Adds the background color to the html element to prevent white background on overscroll.
398+
document.documentElement.classList.add('bg-background');
399+
}
400+
setIsColorSchemeLoaded(true);
401+
hasMounted.current = true;
417402
}, []);
418403
419404
if (!isColorSchemeLoaded) {
@@ -427,6 +412,10 @@ export default function RootLayout() {
427412
</ThemeProvider>
428413
);
429414
}
415+
416+
const useIsomorphicLayoutEffect =
417+
Platform.OS === 'web' && typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
418+
430419
`} />
431420

432421
### Install and Set Up Icons

apps/showcase/app.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"icon": "./assets/images/icon.png",
88
"scheme": "example",
99
"userInterfaceStyle": "automatic",
10+
"newArchEnabled": true,
1011
"runtimeVersion": {
1112
"policy": "appVersion"
1213
},

apps/showcase/app/_layout.tsx

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
2-
import AsyncStorage from '@react-native-async-storage/async-storage';
32
import { Theme, ThemeProvider, DefaultTheme, DarkTheme } from '@react-navigation/native';
43
import { PortalHost } from '@rn-primitives/portal';
54
import { DeprecatedUi } from '@rnr/reusables';
6-
import { SplashScreen, Stack } from 'expo-router';
5+
import { Stack } from 'expo-router';
76
import { StatusBar } from 'expo-status-bar';
87
import * as React from 'react';
98
import { Platform } from 'react-native';
@@ -35,38 +34,23 @@ export const unstable_settings = {
3534
initialRouteName: '(tabs)',
3635
};
3736

38-
// Prevent the splash screen from auto-hiding before getting the color scheme.
39-
SplashScreen.preventAutoHideAsync();
40-
4137
export default function RootLayout() {
42-
const { colorScheme, setColorScheme, isDarkColorScheme } = useColorScheme();
38+
const hasMounted = React.useRef(false);
39+
const { colorScheme, isDarkColorScheme } = useColorScheme();
4340
const [isColorSchemeLoaded, setIsColorSchemeLoaded] = React.useState(false);
4441

45-
React.useEffect(() => {
46-
(async () => {
47-
const theme = await AsyncStorage.getItem('theme');
48-
if (Platform.OS === 'web') {
49-
// Adds the background color to the html element to prevent white background on overscroll.
50-
document.documentElement.classList.add('bg-background');
51-
}
52-
if (!theme) {
53-
setAndroidNavigationBar(colorScheme);
54-
AsyncStorage.setItem('theme', colorScheme);
55-
setIsColorSchemeLoaded(true);
56-
return;
57-
}
58-
const colorTheme = theme === 'dark' ? 'dark' : 'light';
59-
setAndroidNavigationBar(colorTheme);
60-
if (colorTheme !== colorScheme) {
61-
setColorScheme(colorTheme);
42+
useIsomorphicLayoutEffect(() => {
43+
if (hasMounted.current) {
44+
return;
45+
}
6246

63-
setIsColorSchemeLoaded(true);
64-
return;
65-
}
66-
setIsColorSchemeLoaded(true);
67-
})().finally(() => {
68-
SplashScreen.hideAsync();
69-
});
47+
if (Platform.OS === 'web') {
48+
// Adds the background color to the html element to prevent white background on overscroll.
49+
document.documentElement.classList.add('bg-background');
50+
}
51+
setAndroidNavigationBar(colorScheme);
52+
setIsColorSchemeLoaded(true);
53+
hasMounted.current = true;
7054
}, []);
7155

7256
if (!isColorSchemeLoaded) {
@@ -111,6 +95,9 @@ export default function RootLayout() {
11195
);
11296
}
11397

98+
const useIsomorphicLayoutEffect =
99+
Platform.OS === 'web' && typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
100+
114101
function toOptions(name: string) {
115102
const title = name
116103
.split('-')

apps/showcase/components/ThemeToggle.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import AsyncStorage from '@react-native-async-storage/async-storage';
21
import { Pressable, View } from 'react-native';
32
import { setAndroidNavigationBar } from '~/lib/android-navigation-bar';
43
import { MoonStar } from '~/lib/icons/MoonStar';
@@ -8,14 +7,16 @@ import { cn } from '~/lib/utils';
87

98
export function ThemeToggle() {
109
const { isDarkColorScheme, setColorScheme } = useColorScheme();
10+
11+
function toggleColorScheme() {
12+
const newTheme = isDarkColorScheme ? 'light' : 'dark';
13+
setColorScheme(newTheme);
14+
setAndroidNavigationBar(newTheme);
15+
}
16+
1117
return (
1218
<Pressable
13-
onPress={() => {
14-
const newTheme = isDarkColorScheme ? 'light' : 'dark';
15-
setColorScheme(newTheme);
16-
setAndroidNavigationBar(newTheme);
17-
AsyncStorage.setItem('theme', newTheme);
18-
}}
19+
onPress={toggleColorScheme}
1920
className='web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2'
2021
>
2122
{({ pressed }) => (

apps/showcase/package.json

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"dependencies": {
1717
"@gorhom/bottom-sheet": "^4.6.0",
1818
"@hookform/resolvers": "^3.3.4",
19-
"@react-native-async-storage/async-storage": "1.23.1",
2019
"@react-native-community/slider": "4.5.5",
2120
"@react-navigation/material-top-tabs": "^7.0.0",
2221
"@react-navigation/native": "^7.0.0",
@@ -33,31 +32,31 @@
3332
"@tanstack/react-table": "^8.11.7",
3433
"class-variance-authority": "^0.7.0",
3534
"clsx": "^2.1.0",
36-
"expo": "^52.0.7",
35+
"expo": "^52.0.23",
3736
"expo-blur": "~14.0.1",
3837
"expo-clipboard": "~7.0.0",
39-
"expo-font": "~13.0.1",
38+
"expo-font": "~13.0.2",
4039
"expo-haptics": "~14.0.0",
41-
"expo-image": "~2.0.0",
40+
"expo-image": "~2.0.3",
4241
"expo-linear-gradient": "~14.0.1",
43-
"expo-linking": "~7.0.2",
44-
"expo-navigation-bar": "~4.0.3",
45-
"expo-router": "~4.0.5",
46-
"expo-splash-screen": "~0.29.10",
42+
"expo-linking": "~7.0.3",
43+
"expo-navigation-bar": "~4.0.6",
44+
"expo-router": "~4.0.15",
45+
"expo-splash-screen": "~0.29.18",
4746
"expo-status-bar": "~2.0.0",
48-
"expo-system-ui": "~4.0.3",
47+
"expo-system-ui": "~4.0.6",
4948
"lucide-react-native": "^0.378.0",
5049
"nativewind": "^4.1.23",
5150
"react": "18.3.1",
5251
"react-dom": "18.3.1",
5352
"react-hook-form": "^7.49.2",
54-
"react-native": "0.76.2",
53+
"react-native": "0.76.5",
5554
"react-native-calendars": "^1.1302.0",
5655
"react-native-gesture-handler": "~2.20.2",
57-
"react-native-pager-view": "6.4.1",
56+
"react-native-pager-view": "6.5.1",
5857
"react-native-reanimated": "~3.16.1",
5958
"react-native-safe-area-context": "4.12.0",
60-
"react-native-screens": "^4.0.0",
59+
"react-native-screens": "^4.4.0",
6160
"react-native-svg": "15.8.0",
6261
"react-native-tab-view": "^3.5.2",
6362
"react-native-toast-message": "^2.2.0",

packages/reusables/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
"@tsconfig/recommended": "^1.0.1",
191191
"@types/react": "~18.3.12",
192192
"react": "18.3.1",
193-
"react-native": "0.76.2"
193+
"react-native": "0.76.5"
194194
},
195195
"peerDependencies": {
196196
"react": "*",

packages/templates/starter-base/app.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
"icon": "./assets/images/icon.png",
88
"scheme": "myapp",
99
"userInterfaceStyle": "automatic",
10+
"newArchEnabled": true,
1011
"splash": {
1112
"image": "./assets/images/splash.png",
1213
"resizeMode": "contain",
1314
"backgroundColor": "#ffffff"
1415
},
15-
"assetBundlePatterns": ["**/*"],
16+
"assetBundlePatterns": [
17+
"**/*"
18+
],
1619
"ios": {
1720
"supportsTablet": true
1821
},
@@ -27,9 +30,11 @@
2730
"output": "static",
2831
"favicon": "./assets/images/favicon.png"
2932
},
30-
"plugins": ["expo-router"],
33+
"plugins": [
34+
"expo-router"
35+
],
3136
"experiments": {
3237
"typedRoutes": true
3338
}
3439
}
35-
}
40+
}

packages/templates/starter-base/app/_layout.tsx

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import '~/global.css';
22

3-
import AsyncStorage from '@react-native-async-storage/async-storage';
43
import { DarkTheme, DefaultTheme, Theme, ThemeProvider } from '@react-navigation/native';
5-
import { SplashScreen, Stack } from 'expo-router';
4+
import { Stack } from 'expo-router';
65
import { StatusBar } from 'expo-status-bar';
76
import * as React from 'react';
87
import { Platform } from 'react-native';
@@ -26,37 +25,23 @@ export {
2625
ErrorBoundary,
2726
} from 'expo-router';
2827

29-
// Prevent the splash screen from auto-hiding before getting the color scheme.
30-
SplashScreen.preventAutoHideAsync();
31-
3228
export default function RootLayout() {
33-
const { colorScheme, setColorScheme, isDarkColorScheme } = useColorScheme();
29+
const hasMounted = React.useRef(false);
30+
const { colorScheme, isDarkColorScheme } = useColorScheme();
3431
const [isColorSchemeLoaded, setIsColorSchemeLoaded] = React.useState(false);
3532

36-
React.useEffect(() => {
37-
(async () => {
38-
const theme = await AsyncStorage.getItem('theme');
39-
if (Platform.OS === 'web') {
40-
// Adds the background color to the html element to prevent white background on overscroll.
41-
document.documentElement.classList.add('bg-background');
42-
}
43-
if (!theme) {
44-
AsyncStorage.setItem('theme', colorScheme);
45-
setIsColorSchemeLoaded(true);
46-
return;
47-
}
48-
const colorTheme = theme === 'dark' ? 'dark' : 'light';
49-
if (colorTheme !== colorScheme) {
50-
setColorScheme(colorTheme);
51-
setAndroidNavigationBar(colorTheme);
52-
setIsColorSchemeLoaded(true);
53-
return;
54-
}
55-
setAndroidNavigationBar(colorTheme);
56-
setIsColorSchemeLoaded(true);
57-
})().finally(() => {
58-
SplashScreen.hideAsync();
59-
});
33+
useIsomorphicLayoutEffect(() => {
34+
if (hasMounted.current) {
35+
return;
36+
}
37+
38+
if (Platform.OS === 'web') {
39+
// Adds the background color to the html element to prevent white background on overscroll.
40+
document.documentElement.classList.add('bg-background');
41+
}
42+
setAndroidNavigationBar(colorScheme);
43+
setIsColorSchemeLoaded(true);
44+
hasMounted.current = true;
6045
}, []);
6146

6247
if (!isColorSchemeLoaded) {
@@ -79,3 +64,6 @@ export default function RootLayout() {
7964
</ThemeProvider>
8065
);
8166
}
67+
68+
const useIsomorphicLayoutEffect =
69+
Platform.OS === 'web' && typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;

packages/templates/starter-base/components/ThemeToggle.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import AsyncStorage from '@react-native-async-storage/async-storage';
21
import { Pressable, View } from 'react-native';
32
import { setAndroidNavigationBar } from '~/lib/android-navigation-bar';
43
import { MoonStar } from '~/lib/icons/MoonStar';
@@ -8,14 +7,16 @@ import { cn } from '~/lib/utils';
87

98
export function ThemeToggle() {
109
const { isDarkColorScheme, setColorScheme } = useColorScheme();
10+
11+
function toggleColorScheme() {
12+
const newTheme = isDarkColorScheme ? 'light' : 'dark';
13+
setColorScheme(newTheme);
14+
setAndroidNavigationBar(newTheme);
15+
}
16+
1117
return (
1218
<Pressable
13-
onPress={() => {
14-
const newTheme = isDarkColorScheme ? 'light' : 'dark';
15-
setColorScheme(newTheme);
16-
setAndroidNavigationBar(newTheme);
17-
AsyncStorage.setItem('theme', newTheme);
18-
}}
19+
onPress={toggleColorScheme}
1920
className='web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2'
2021
>
2122
{({ pressed }) => (

0 commit comments

Comments
 (0)