Skip to content

Commit 03af5b8

Browse files
authored
Merge pull request #354 from jpudysz/feature/provider
feat: react provider for Unistyles
2 parents 829290b + f6906d7 commit 03af5b8

File tree

17 files changed

+319
-242
lines changed

17 files changed

+319
-242
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ npx expo prebuild
7575
<a href="https://github.com/happyfloat">
7676
<img src="https://avatars.githubusercontent.com/u/186333704?s=200&v=4" height="70px" width="70px" alt="happyfloat" />
7777
</a>
78+
<a href="https://github.com">
79+
<img src="https://avatars.githubusercontent.com/u/138339?v=4" height="70px" width="70px" alt="anonymous" />
80+
</a>
81+
<a href="https://github.com/hyoban">
82+
<img src="https://avatars.githubusercontent.com/u/38493346?v=4" height="70px" width="70px" alt="hyoban" />
83+
</a>
7884

7985
## Past sponsors
8086

docs/.astro/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"_variables": {
3-
"lastUpdateCheck": 1727846556761
3+
"lastUpdateCheck": 1732289968045
44
}
55
}

docs/.astro/types.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ declare module 'astro:content' {
262262
collection: "docs";
263263
data: InferEntrySchema<"docs">
264264
} & { render(): Render[".mdx"] };
265+
"reference/unistyles-provider.mdx": {
266+
id: "reference/unistyles-provider.mdx";
267+
slug: "reference/unistyles-provider";
268+
body: string;
269+
collection: "docs";
270+
data: InferEntrySchema<"docs">
271+
} & { render(): Render[".mdx"] };
265272
"reference/unistyles-registry.mdx": {
266273
id: "reference/unistyles-registry.mdx";
267274
slug: "reference/unistyles-registry";

docs/astro.config.mjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export default defineConfig({
6363
label: 'useStyles',
6464
link: '/reference/use-styles/'
6565
},
66+
{
67+
label: 'UnistylesProvider',
68+
link: '/reference/unistyles-provider/',
69+
badge: 'New'
70+
},
6671
{
6772
label: 'Dynamic functions',
6873
link: '/reference/dynamic-functions/'
@@ -105,8 +110,7 @@ export default defineConfig({
105110
},
106111
{
107112
label: 'Unistyles Runtime',
108-
link: '/reference/unistyles-runtime/',
109-
badge: 'Updated'
113+
link: '/reference/unistyles-runtime/'
110114
},
111115
{
112116
label: 'Content size category',
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
title: Unistyles Provider
3+
---
4+
5+
import Seo from '../../../components/Seo.astro'
6+
import Badge from '../../../components/Badge.astro'
7+
8+
<Seo
9+
seo={{
10+
title: 'Unistyles Provider',
11+
description: 'React Context in react-native-unistyles'
12+
}}
13+
>
14+
15+
<Badge label="All platforms" />
16+
<Badge label="2.20.0" />
17+
18+
If you app has dozens of screens and hundereds of `useStyles` hooks, you can encounter some performance issues and delays eg. when changing the theme.
19+
20+
To solve this problem, Unistyles 2.20.0 introduces a new component called `UnistylesProvider`.
21+
22+
### Usage
23+
24+
In order to use `UnistylesProvider`, you just need to wrap your app with it:
25+
26+
```diff lang="tsx"
27+
import { UnistylesProvider } from 'react-native-unistyles'
28+
29+
export const App: React.FunctionComponent = () => (
30+
+ <UnistylesProvider>
31+
<YourApp />
32+
+ </UnistylesProvider>
33+
)
34+
```
35+
36+
This line of code will cancel all subscriptions from your `useStyles` hooks and switch the source of truth to the `UnistylesProvider`.
37+
38+
### How fast is it?
39+
40+
Re-rendering 1000 boxes with 1000 useStyles hooks (dev mode):
41+
42+
| Version | Time |
43+
| --- | --- |
44+
| with Provider | ~700 ms |
45+
| without Provider | ~4.5 seconds! |
46+
47+
**It can speed your app up to x6-7 times!**
48+
49+
:::caution
50+
This feature is not enabled by default, due to breaking changes in the default behavior of `useStyles`.
51+
:::
52+
53+
</Seo>

examples/expo/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const App: React.FunctionComponent = () => (
2828
<Stack.Screen name={DemoNames.NoThemes} component={Screens.NoThemesScreen} />
2929
<Stack.Screen name={DemoNames.SingleTheme} component={Screens.SingleThemeScreen} />
3030
<Stack.Screen name={DemoNames.TwoThemes} component={Screens.TwoThemesScreen} />
31-
<Stack.Screen name={DemoNames.TwoThemesWithProvider} component={Screens.TwoThemesWithProviderScreen} />
3231
<Stack.Screen name={DemoNames.LightDarkThemes} component={Screens.LightDarkThemesScreen} />
3332
<Stack.Screen name={DemoNames.MultipleThemes} component={Screens.MultipleThemesScreen} />
3433
<Stack.Screen name={DemoNames.MultipleThemesAdaptive} component={Screens.MultipleThemesAdaptiveScreen} />
@@ -60,6 +59,7 @@ export const App: React.FunctionComponent = () => (
6059
<Stack.Screen name={DemoNames.UpdateTheme} component={Screens.UpdateThemeScreen} />
6160
<Stack.Screen name={DemoNames.AndroidStatusBarNavigationBar} component={Screens.AndroidStatusBarNavigationBarScreen} />
6261
<Stack.Screen name={DemoNames.Keyboard} component={Screens.KeyboardScreen} />
62+
<Stack.Screen name={DemoNames.ReactProvider} component={Screens.ReactProviderScreen} />
6363
</Stack.Navigator>
6464
</NavigationContainer>
6565
</SafeAreaProvider>

examples/expo/src/common/navigation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export enum DemoNames {
3232
AndroidStatusBarNavigationBar = 'AndroidStatusBarNavigationBarScreen',
3333
Layout = 'LayoutScreen',
3434
Keyboard = 'KeyboardScreen',
35-
TwoThemesWithProvider = 'TwoThemesWithProvider',
35+
ReactProvider = 'ReactProvider',
3636
}
3737

3838
export type DemoStackParams = {
@@ -67,7 +67,7 @@ export type DemoStackParams = {
6767
[DemoNames.AndroidStatusBarNavigationBar]: undefined,
6868
[DemoNames.Layout]: undefined,
6969
[DemoNames.Keyboard]: undefined
70-
[DemoNames.TwoThemesWithProvider]: undefined,
70+
[DemoNames.ReactProvider]: undefined,
7171
}
7272

7373
export type NavigationProps<S extends DemoNames = DemoNames.Home> = NavigationProp<DemoStackParams, S>

examples/expo/src/components/Button.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ type ButtonProps = {
55
color?: string,
66
textColor?: string,
77
title: string,
8+
alignCenter?: boolean,
89
onPress: VoidFunction
910
}
1011

1112
export const Button: React.FunctionComponent<ButtonProps> = ({
1213
onPress,
1314
title,
15+
alignCenter = false,
1416
textColor = 'white',
1517
color = '#29cbfa'
1618
}) => (
@@ -22,7 +24,7 @@ export const Button: React.FunctionComponent<ButtonProps> = ({
2224
borderRadius: 10
2325
}}
2426
>
25-
<Text style={{ color: textColor }}>
27+
<Text style={{ color: textColor, textAlign: alignCenter ? 'center' : undefined }}>
2628
{title}
2729
</Text>
2830
</Pressable>

examples/expo/src/examples/HomeScreen.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,23 +71,6 @@ export const HomeScreen = () => {
7171
navigation.navigate(DemoNames.TwoThemes)
7272
}}
7373
/>
74-
<DemoLink
75-
description="Two themes with provider"
76-
onPress={() => {
77-
UnistylesRegistry
78-
.addThemes({
79-
light: lightTheme,
80-
premium: premiumTheme
81-
// we need to cast it to UnistylesThemes as we already registered 3 themes with TypeScript under styles/index.ts,
82-
// but we want to demonstrate how to register two themes
83-
} as UnistylesThemes)
84-
.addConfig({
85-
initialTheme: 'light'
86-
})
87-
88-
navigation.navigate(DemoNames.TwoThemesWithProvider)
89-
}}
90-
/>
9174
<DemoLink
9275
description="Light/Dark themes"
9376
onPress={() => {
@@ -525,6 +508,23 @@ export const HomeScreen = () => {
525508
navigation.navigate(DemoNames.Keyboard)
526509
}}
527510
/>
511+
<DemoLink
512+
description="React Provider"
513+
onPress={() => {
514+
UnistylesRegistry
515+
.addThemes({
516+
light: lightTheme,
517+
dark: darkTheme
518+
// we need to cast it to UnistylesThemes as we already registered 3 themes with TypeScript under styles/index.ts,
519+
// but we want to demonstrate how to register two themes
520+
} as UnistylesThemes)
521+
.addConfig({
522+
initialTheme: 'light'
523+
})
524+
525+
navigation.navigate(DemoNames.ReactProvider)
526+
}}
527+
/>
528528
</DemoGroup>
529529
<DemoGroup title="Benchmark">
530530
<DemoLink
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React from 'react'
2+
import { ScrollView, Text, View } from 'react-native'
3+
import { createStyleSheet, useStyles, UnistylesProvider, UnistylesRuntime } from 'react-native-unistyles'
4+
import { Button, DemoScreen } from '../components'
5+
6+
type BoxProps = {
7+
index: number
8+
}
9+
10+
export const ReactProviderScreen: React.FunctionComponent = () => (
11+
<UnistylesProvider>
12+
<ReactProviderScreenScreenContent />
13+
</UnistylesProvider>
14+
)
15+
16+
const Box: React.FunctionComponent<BoxProps> = ({ index }) => {
17+
const { styles } = useStyles(stylesheet)
18+
19+
return (
20+
<View style={styles.box}>
21+
<Text style={styles.text}>
22+
{index + 1}
23+
</Text>
24+
</View>
25+
)
26+
}
27+
28+
const ReactProviderScreenScreenContent: React.FunctionComponent = () => {
29+
const { styles } = useStyles(stylesheet)
30+
31+
return (
32+
<DemoScreen>
33+
<View style={styles.container}>
34+
<Text style={styles.title}>
35+
This screen uses UnistylesProvider that may help if your app have hundreds of useStyles hooks.
36+
</Text>
37+
<Text style={styles.title}>
38+
Rendering 1000 boxes with 1000 useStyles hooks:
39+
</Text>
40+
<ScrollView showsVerticalScrollIndicator={false}>
41+
{Array.from({ length: 1000 }).map((_, index) => (
42+
<Box
43+
key={index}
44+
index={index}
45+
/>
46+
))}
47+
</ScrollView>
48+
</View>
49+
<View style={styles.absolutView}>
50+
<Button
51+
alignCenter
52+
title="Change theme"
53+
onPress={() => {
54+
UnistylesRuntime.themeName === 'light'
55+
? UnistylesRuntime.setTheme('dark')
56+
: UnistylesRuntime.setTheme('light')
57+
}}
58+
/>
59+
</View>
60+
</DemoScreen>
61+
)
62+
}
63+
64+
const stylesheet = createStyleSheet(theme => ({
65+
container: {
66+
flex: 1,
67+
justifyContent: 'center',
68+
alignItems: 'center',
69+
paddingHorizontal: 20,
70+
backgroundColor: theme.colors.backgroundColor,
71+
rowGap: 20
72+
},
73+
title: {
74+
fontSize: 20,
75+
textAlign: 'center',
76+
color: theme.colors.typography
77+
},
78+
text: {
79+
textAlign: 'center',
80+
color: theme.colors.typography
81+
},
82+
boxes: {
83+
flexDirection: 'row'
84+
},
85+
box: {
86+
height: 40,
87+
width: 40,
88+
justifyContent: 'center',
89+
marginBottom: 10,
90+
backgroundColor: theme.colors.accent
91+
},
92+
absolutView: {
93+
position: 'absolute',
94+
bottom: 0,
95+
left: 50,
96+
right: 50,
97+
height: 100
98+
}
99+
}))

0 commit comments

Comments
 (0)