|
1 | | -import React, { useEffect } from 'react'; |
2 | | -import { StyleSheet, Text, View, Button } from 'react-native'; |
| 1 | +import React, { useCallback, useSyncExternalStore } from 'react'; |
| 2 | +import { StyleSheet, Text, View, Button, TextInput } from 'react-native'; |
3 | 3 | import { |
4 | | - createNativeStackNavigator, |
5 | | - type NativeStackScreenProps, |
6 | | -} from '@react-navigation/native-stack'; |
7 | | -import ReactNativeBrownfield from '@callstack/react-native-brownfield'; |
8 | | -import { NavigationContainer } from '@react-navigation/native'; |
| 4 | + subscribe, |
| 5 | + getSnapshot, |
| 6 | + setState, |
| 7 | +} from '@callstack/react-native-brownfield'; |
| 8 | +import type { BrownfieldStore } from './brownfield-store.schema'; |
9 | 9 |
|
10 | | -const getRandomValue = () => Math.round(Math.random() * 255); |
11 | | -const getRandomTheme = () => { |
12 | | - const primary = [getRandomValue(), getRandomValue(), getRandomValue()]; |
13 | | - const secondary = [ |
14 | | - 255 - (primary?.[0] || 0), |
15 | | - 255 - (primary?.[1] || 0), |
16 | | - 255 - (primary?.[2] || 0), |
17 | | - ]; |
| 10 | +const STORE_KEY = 'BrownfieldStore'; |
18 | 11 |
|
19 | | - return { |
20 | | - primary: `rgb(${primary[0]}, ${primary[1]}, ${primary[2]})`, |
21 | | - secondary: `rgb(${secondary[0]}, ${secondary[1]}, ${secondary[2]})`, |
22 | | - }; |
23 | | -}; |
| 12 | +function useBrownfieldStore<T>(key: string): T { |
| 13 | + const sub = useCallback( |
| 14 | + (listener: () => void) => subscribe(key, listener), |
| 15 | + [key] |
| 16 | + ); |
| 17 | + const snap = useCallback(() => getSnapshot<T>(key), [key]); |
| 18 | + return useSyncExternalStore(sub, snap, snap); |
| 19 | +} |
24 | 20 |
|
25 | | -type Props = NativeStackScreenProps<RootStackParamList, 'Home'>; |
| 21 | +function HomeScreen() { |
| 22 | + const state = useBrownfieldStore<BrownfieldStore>(STORE_KEY); |
26 | 23 |
|
27 | | -function HomeScreen({ navigation, route }: Props) { |
28 | | - const colors = route.params?.theme || getRandomTheme(); |
| 24 | + const handleIncrement = () => { |
| 25 | + setState<BrownfieldStore>(STORE_KEY, { counter: state.counter + 1 }); |
| 26 | + }; |
29 | 27 |
|
30 | | - useEffect(() => { |
31 | | - const unsubscribe = navigation.addListener('focus', () => { |
32 | | - const isFirstRoute = !navigation.canGoBack(); |
33 | | - ReactNativeBrownfield.setNativeBackGestureAndButtonEnabled(isFirstRoute); |
34 | | - }); |
35 | | - return unsubscribe; |
36 | | - }, [navigation]); |
| 28 | + const handleSetHasError = () => { |
| 29 | + setState<BrownfieldStore>(STORE_KEY, { hasError: !state.hasError }); |
| 30 | + }; |
37 | 31 |
|
38 | 32 | return ( |
39 | | - <View style={[styles.container, { backgroundColor: colors.primary }]}> |
40 | | - <Text style={[styles.text, { color: colors.secondary }]}> |
41 | | - React Native Screen |
| 33 | + <View style={styles.container}> |
| 34 | + <Text style={styles.title}>React Native Side</Text> |
| 35 | + <Text style={styles.text}>Count: {state.counter}</Text> |
| 36 | + <Text style={styles.text}> |
| 37 | + Has error: {state.hasError ? 'true' : 'false'} |
42 | 38 | </Text> |
43 | 39 |
|
44 | | - <Button |
45 | | - onPress={() => { |
46 | | - navigation.push('Home', { |
47 | | - theme: getRandomTheme(), |
48 | | - }); |
49 | | - }} |
50 | | - color={colors.secondary} |
51 | | - title="Push next screen" |
| 40 | + <TextInput |
| 41 | + style={styles.input} |
| 42 | + value={state.user} |
| 43 | + onChangeText={(text) => |
| 44 | + setState<BrownfieldStore>(STORE_KEY, { user: text }) |
| 45 | + } |
| 46 | + placeholder="User name" |
52 | 47 | /> |
53 | 48 |
|
54 | | - <Button |
55 | | - onPress={() => { |
56 | | - if (navigation.canGoBack()) { |
57 | | - navigation.goBack(); |
58 | | - } else { |
59 | | - ReactNativeBrownfield.popToNative(true); |
60 | | - } |
61 | | - }} |
62 | | - color={colors.secondary} |
63 | | - title="Go back" |
64 | | - /> |
| 49 | + <Button onPress={handleIncrement} title="Increment" /> |
| 50 | + <Button onPress={handleSetHasError} title="Toggle Has Error" /> |
65 | 51 | </View> |
66 | 52 | ); |
67 | 53 | } |
68 | | -type RootStackParamList = { |
69 | | - Home: { theme: { primary: string; secondary: string } }; |
70 | | -}; |
71 | | - |
72 | | -const Stack = createNativeStackNavigator<RootStackParamList>(); |
73 | | - |
74 | 54 | export default function App() { |
75 | | - return ( |
76 | | - <NavigationContainer> |
77 | | - <Stack.Navigator> |
78 | | - <Stack.Screen name="Home" component={HomeScreen} /> |
79 | | - </Stack.Navigator> |
80 | | - </NavigationContainer> |
81 | | - ); |
| 55 | + return <HomeScreen />; |
82 | 56 | } |
83 | 57 |
|
84 | 58 | const styles = StyleSheet.create({ |
85 | 59 | container: { |
86 | 60 | flex: 1, |
87 | 61 | justifyContent: 'center', |
88 | 62 | alignItems: 'center', |
| 63 | + backgroundColor: '#f0f0f0', |
89 | 64 | }, |
90 | | - text: { |
91 | | - fontSize: 30, |
| 65 | + title: { |
| 66 | + fontSize: 20, |
92 | 67 | fontWeight: 'bold', |
93 | | - margin: 10, |
| 68 | + marginBottom: 20, |
| 69 | + }, |
| 70 | + text: { |
| 71 | + fontSize: 16, |
| 72 | + margin: 5, |
| 73 | + }, |
| 74 | + input: { |
| 75 | + borderWidth: 1, |
| 76 | + borderColor: '#ccc', |
| 77 | + borderRadius: 8, |
| 78 | + padding: 10, |
| 79 | + width: 200, |
| 80 | + marginVertical: 10, |
| 81 | + backgroundColor: '#fff', |
94 | 82 | }, |
95 | 83 | }); |
0 commit comments