├── .gitignore
├── App.fs
├── App.fsproj
├── README.md
├── index.js
├── minimum
└── README.md
├── navigation
├── App.fs
├── App.fsproj
├── Counter.fs
└── README.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .ionide/
2 | .fable/
3 | node_modules/
4 | bin/
5 | obj/
--------------------------------------------------------------------------------
/App.fs:
--------------------------------------------------------------------------------
1 | module App
2 |
3 | open Elmish
4 | open Elmish.React
5 | open Elmish.ReactNative
6 | open Fable.ReactNative
7 |
8 | type Model = {
9 | Counter : int
10 | }
11 |
12 | type Message =
13 | | Increment
14 |
15 | let init () = {Counter = 0}, Cmd.none
16 |
17 | let update msg model =
18 | match msg with
19 | | Increment ->
20 | {model with Counter = model.Counter + 1}, Cmd.none
21 |
22 | module R = Fable.ReactNative.Helpers
23 | module P = Fable.ReactNative.Props
24 | open Fable.ReactNative.Props
25 |
26 | let view model dispatch =
27 |
28 | R.view [
29 | P.ViewProperties.Style [
30 | P.FlexStyle.Flex 1.0
31 | P.FlexStyle.JustifyContent JustifyContent.Center
32 | P.BackgroundColor "#131313" ]
33 | ] [
34 |
35 |
36 | R.text [
37 | P.TextProperties.Style [ P.Color "#ffffff" ]
38 | ] "Press me"
39 | |> R.touchableHighlightWithChild [
40 | P.TouchableHighlightProperties.Style [
41 | P.FlexStyle.Padding (R.dip 10.)
42 | ]
43 | P.TouchableHighlightProperties.UnderlayColor "#f6f6f6"
44 | OnPress (fun _ -> dispatch Increment)
45 | ]
46 |
47 | R.text [
48 | P.TextProperties.Style [
49 | P.Color "#ffffff"
50 | P.FontSize 30.
51 | P.TextAlign P.TextAlignment.Center
52 | ]
53 | ] (string model.Counter)
54 | ]
55 |
56 | Program.mkProgram init update view
57 | |> Program.withConsoleTrace
58 | |> Program.withReactNative "Your project name"
59 | |> Program.run
--------------------------------------------------------------------------------
/App.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # How-to: Set up React Native with F# and Fable
2 |
3 | This is a step-by-step guide which aims to help you set up a React Native project with F# and Fable. The [Fable compiler](https://github.com/fable-compiler/Fable) generates JavaScript from your F# source code, and enables you to write React Native apps targeting iOS and Android almost completely through F#! Since the Fable compiler is just generating pure JavaScript from our F# source code we should be able to target any platform through React Native (Windows, MacOS, Web), though i have not tested this myself. Feel free to share your own experience with any of these platforms.
4 |
5 | Sample files for getting started can be found within this repository.
6 |
7 | ## Requirements
8 | - React Native
9 | - Watchman
10 | - Node.js
11 | - .NET Core >= 3.0
12 | - npm
13 |
14 | # Setup a new React Native project
15 |
16 | This how-to will not go into detail on how to install React Native and how React Native works. When you have React Native installed you can create a new project by running `react-native init `
17 |
18 | You should test that your basic React Native project compiles/runs before moving further.
19 |
20 | `npx react-native run-ios` or `npx react-native run-android`
21 |
22 | # Setup the F# project
23 | Create a folder to hold your F# project and add a `.fsproj` file with a simple `.fs` file. In this example i will create a `src` folder in the project root directory which contains the files `App.fsproj` and `App.fs`.
24 |
25 | ## `App.fsproj`
26 | ```xml
27 |
28 |
29 |
30 | netstandard2.1
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ```
44 |
45 | ## `App.fs`
46 | ```fsharp
47 | module App
48 |
49 | open Elmish
50 | open Elmish.React
51 | open Elmish.ReactNative
52 | open Fable.ReactNative
53 |
54 | // A very simple app which increments a number when you press a button
55 |
56 | type Model = {
57 | Counter : int
58 | }
59 |
60 | type Message =
61 | | Increment
62 |
63 | let init () = {Counter = 0}, Cmd.none
64 |
65 | let update msg model =
66 | match msg with
67 | | Increment ->
68 | {model with Counter = model.Counter + 1}, Cmd.none
69 |
70 | module R = Fable.ReactNative.Helpers
71 | module P = Fable.ReactNative.Props
72 | open Fable.ReactNative.Props
73 |
74 | let view model dispatch =
75 | R.view [
76 | P.ViewProperties.Style [
77 | P.FlexStyle.Flex 1.0
78 | P.FlexStyle.JustifyContent JustifyContent.Center
79 | P.BackgroundColor "#131313" ]
80 | ] [
81 |
82 |
83 | R.text [
84 | P.TextProperties.Style [ P.Color "#ffffff" ]
85 | ] "Press me"
86 | |> R.touchableHighlightWithChild [
87 | P.TouchableHighlightProperties.Style [
88 | P.FlexStyle.Padding (R.dip 10.)
89 | ]
90 | P.TouchableHighlightProperties.UnderlayColor "#f6f6f6"
91 | OnPress (fun _ -> dispatch Increment)
92 | ]
93 |
94 | R.text [
95 | P.TextProperties.Style [
96 | P.Color "#ffffff"
97 | P.FontSize 30.
98 | P.TextAlign P.TextAlignment.Center
99 | ]
100 | ] (string model.Counter)
101 | ]
102 |
103 | Program.mkProgram init update view
104 | |> Program.withConsoleTrace
105 | |> Program.withReactNative "Your project name" // CHANGE ME
106 | |> Program.run
107 | ```
108 |
109 | IMPORTANT: Feed the name of your project in `Program.withReactNative` (the same you used for `react-native init` )
110 |
111 | # Install Fable tool
112 |
113 | `dotnet new tool-manifest && dotnet tool install fable`
114 |
115 | # Install npm-packages
116 |
117 | Install the [@babel/preset-env](https://www.npmjs.com/package/@babel/preset-env) npm-module as a dev-dependency. See the documentation of these for further info.
118 |
119 | `npm install --save-dev @babel/preset-env`
120 |
121 | You will also need to install the [buffer](https://www.npmjs.com/package/buffer) npm-module, along with the [@react-native-community/netinfo](https://www.npmjs.com/package/@react-native-community/netinfo) module which is required by Fable.React.Native.
122 |
123 | `npm install buffer @react-native-community/netinfo`
124 |
125 | You can now compile your F# project to Javascript by simply running `dotnet fable ./src -o ./out`
126 | (Note the `-o` parameter specifying the output folder to dump the .js files)
127 |
128 | If you get a compilation error it is likely to be caused by your `babel.config.js` file, and i've experienced that the easiest way to get rid of this i simply by deleting the `babel.config.js` file altogether. You can also provide a configuration file as shown below. However, someone with a better Babel understanding than me could probably provide a better configuration/setup (suggestions welcomed).
129 |
130 | #### Tips:
131 | ```json
132 | "build": "dotnet fable ./src -o ./out",
133 | "watch": "dotnet fable watch ./src -o ./out"
134 | ```
135 | Add the above JSON to the `scripts` section of the `packages.json` file and simply call `npm run build` to compile. Run `npm run watch` in order to watch for changes and enable hot-reloading as you change your F# code.
136 |
137 | # Importing the generated JavaScript
138 | Now you can compile your F# code to JavaScript and dump it to a folder (`./out` used in this example).
139 |
140 | 1. Delete your default `App.js` file in the root directory.
141 | 2. Update your `index.js` file:
142 | ```js
143 | /**
144 | * @format
145 | */
146 |
147 | import { AppRegistry } from 'react-native';
148 | import * as App from './out/App';
149 | import { name as appName } from './app.json';
150 | ```
151 | Notice that we import App from our generated files in the `out` folder. The app registration call is also removed, as this is now handled in our F# code.
152 |
153 | # You're good to go!
154 | 1. Compile F# to JavaScript and watch for changes
155 | - `dotnet fable watch ./src -o ./out`
156 | - or `npm run watch` if you altered the `scripts` section of `packages.json`
157 | 2. Run app
158 | - `npx react-native run-ios|android`
159 | 3. Watch as the app updates along with your F# code. Enjoy!
160 |
161 | # More
162 | - For larger apps you might want to opt out of Elmish and include navigation. Take a look at the following [how-to](navigation)
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import { AppRegistry } from 'react-native';
6 | import * as App from './out/App';
7 | import { name as appName } from './app.json';
--------------------------------------------------------------------------------
/minimum/README.md:
--------------------------------------------------------------------------------
1 | # React Native with Fable from scratch
2 |
3 |
--------------------------------------------------------------------------------
/navigation/App.fs:
--------------------------------------------------------------------------------
1 | module App
2 |
3 | open Fable.ReactNative.Navigation
4 |
5 | module R = Fable.ReactNative.Helpers
6 | module P = Fable.ReactNative.Props
7 | open Fable.ReactNative.Props
8 |
9 | let homePage (nav : Types.INavigation<_>) =
10 | R.view [
11 | P.ViewProperties.Style [
12 | P.FlexStyle.Flex 1.
13 | P.FlexStyle.JustifyContent JustifyContent.Center
14 | P.FlexStyle.AlignItems ItemAlignment.Center
15 | ]
16 | ] [
17 | R.text [] "This is the home screen"
18 |
19 | R.touchableOpacity [
20 | OnPress(fun _ ->
21 | // push a new instance of the counter screen to the stack
22 | nav.navigation.push "counter"
23 | )
24 | ] [
25 | R.text [
26 | P.TextProperties.Style [
27 | P.FlexStyle.MarginTop (R.pct 5.)
28 | ]
29 | ] "Open counter screen"
30 | ]
31 | ]
32 |
33 | let render () =
34 | navigationContainer [] [
35 | Stack.navigator [
36 | Stack.NavigatorProps.InitialRouteName "home"
37 | ] [
38 | Stack.screen "home" homePage [] []
39 | Stack.screen "counter" Counter.counter [
40 | Stack.ScreenProps.InitialParams ({Initial = None} : Counter.CounterProps)
41 | ] []
42 | ]
43 | ]
44 |
45 | // Update app name (react-native init )
46 | Helpers.registerApp "You app name" (render ())
--------------------------------------------------------------------------------
/navigation/App.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/navigation/Counter.fs:
--------------------------------------------------------------------------------
1 | module Counter
2 |
3 | open Fable.ReactNative.Navigation
4 |
5 | type CounterProps = {
6 | Initial : int option
7 | }
8 |
9 | type private Model = {
10 | Navigation : Types.INavigation
11 | Counter : int
12 | }
13 | and private Message =
14 | | Increment
15 | | Decrement
16 |
17 | let private init nav = {
18 | Navigation = nav
19 | Counter =
20 | match nav.route.``params``.Initial with
21 | | None -> 0
22 | | Some i -> i
23 | }
24 |
25 | let private update model msg =
26 | match msg with
27 | | Increment -> {model with Counter = model.Counter + 1 }
28 | | Decrement -> {model with Counter = model.Counter - 1 }
29 |
30 | let private delayedIncrement dispatch =
31 | promise {
32 | do! Promise.sleep 2000
33 | dispatch Increment
34 | }
35 |
36 | module R = Fable.ReactNative.Helpers
37 | module P = Fable.ReactNative.Props
38 | open Fable.ReactNative.Props
39 |
40 | let private buttonStyle : IStyle list = [
41 | P.Color "#fff"
42 | P.FlexStyle.MarginTop (R.pct 5.)
43 | P.FlexStyle.Padding (R.pct 2.)
44 | P.BorderWidth 1.
45 | P.BorderColor "#fff"
46 | ]
47 |
48 | let private view model dispatch =
49 | R.view [
50 | P.ViewProperties.Style [
51 | P.FlexStyle.Flex 1.0
52 | P.FlexStyle.JustifyContent JustifyContent.Center
53 | P.FlexStyle.AlignItems ItemAlignment.Center
54 | P.BackgroundColor "#131313"
55 | ]
56 | ] [
57 |
58 | R.touchableOpacity [
59 | OnPress(fun _ -> dispatch Increment)
60 | ] [
61 | R.text [P.TextProperties.Style buttonStyle] "Increment"
62 | ]
63 |
64 | R.touchableOpacity [
65 | OnPress(fun _ -> dispatch Decrement)
66 | ] [
67 | R.text [P.TextProperties.Style buttonStyle ] "Decrement"
68 | ]
69 |
70 | R.touchableOpacity [
71 | OnPress(fun _ ->
72 | delayedIncrement dispatch
73 | |> Promise.start
74 | )
75 | ] [
76 | R.text [P.TextProperties.Style buttonStyle] "Delayed Increment"
77 | ]
78 |
79 | R.text [
80 | P.TextProperties.Style [
81 | P.Color "#ffffff"
82 | P.FontSize 30.
83 | P.TextAlign P.TextAlignment.Center
84 | P.FlexStyle.MarginTop (R.pct 4.)
85 | ]
86 | ] (string model.Counter)
87 |
88 | R.touchableOpacity [
89 | OnPress (fun _ ->
90 | let props = {Initial = Some (model.Counter * 2)}
91 | pushWithData model.Navigation "counter" props
92 | )
93 | ] [
94 | R.text [
95 | P.TextProperties.Style buttonStyle
96 | ] "Open counter again with double current count"
97 | ]
98 | ]
99 |
100 | let counter (navigation : Types.INavigation ) =
101 | Fable.React.FunctionComponent.Of(fun (props : {| nav : Types.INavigation |} ) ->
102 | let initialModel = init props.nav
103 | let model = Fable.React.HookBindings.Hooks.useReducer(update, initialModel)
104 |
105 | view model.current model.update
106 | ) {|nav = navigation |}
--------------------------------------------------------------------------------
/navigation/README.md:
--------------------------------------------------------------------------------
1 | # Building apps with F# and Fable with smooth looking navigation
2 |
3 | Even though Elmish is great for building single page apps, it does not play too well with large apps and navigation. If you have ever tried combining Elmish with classic app-navigation, you've probably already ripped half your hair out. Pushing multiple new screens atop your current view while keeping your navigation history and state, quickly becomes way more complicated that it has too be while it looks quirky.
4 |
5 | Using [React Navigation](https://reactnavigation.org) you can add professional navigation handling which looks and feels like the common user expects from a modern app. By combining with React Hooks, like `useReducer`, we can still hold on to a similar MVU-design as we are used to with Elmish.
6 |
7 | This example implements a similar counter app like the Elmish sample. The counter screen is navigable and can be pushed multiple times, with the doubled value of the current counter, while keeping history and screen state.
8 |
9 | ## Setup a new React Native project
10 |
11 | Start a standard new React Native project with `react-native init `
12 |
13 | ## Setup the F# project
14 | Create a folder to hold your F# project and add the files `App.fsproj`, `App.fs` and `Counter.fs`. In this example i will create a `src` folder in the project root directory which contains the files `App.fsproj` and `App.fs`.
15 |
16 | ## Add npm modules
17 |
18 | In addition to the npm and NuGet setup from the initial how-to you will have to add the following npm-packages for React Navigation.
19 |
20 | `npm install @react-navigation/native @react-navigation/stack @react-navigation/bottom-tabs react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-screens @react-native-community/masked-view`
21 |
22 | ### `App.fsproj`
23 | ```xml
24 |
25 |
26 |
27 | netstandard2.1
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ```
41 |
42 | Notice that the Elmish references are removed and a reference to `Fable.ReactNative.Navigation` is added, this will provide us with Fable bindings for the React Navigation library.
43 |
44 | ### `App.fs`
45 |
46 | This app implements a similar counter like the Elmish-based how-to. However, in this example we will start the app at a home page and navigate to the counter using React Navigation. You will need to register your app with the name given at `react-native init` and provide a initial render function. This will register your navigation stack and screens which will be dispatched by React Navigation when navigating through the app.
47 |
48 | Functions rendering from React Navigation calls can have a signature of `unit -> ReactElement` or `INavigation<'T> -> unit` where `'T` is the type of data you expect to be pushed with the screen. The `homePage` function renders out initial home page, here we grab the navigation object as we want to use it to navigate further with it. We do not really care about the properties here.
49 |
50 | ```fsharp
51 | module App
52 |
53 | open Fable.ReactNative.Navigation
54 |
55 | module R = Fable.ReactNative.Helpers
56 | module P = Fable.ReactNative.Props
57 | open Fable.ReactNative.Props
58 |
59 | // render function for the home screen
60 | // the nav argument is added since we will be using it for navigation
61 | // let homePage () = would also work if we did not need the navigaiton prop
62 | let homePage (nav : Types.INavigation<_>) =
63 | R.view [
64 | P.ViewProperties.Style [
65 | P.FlexStyle.Flex 1.
66 | P.FlexStyle.JustifyContent JustifyContent.Center
67 | P.FlexStyle.AlignItems ItemAlignment.Center
68 | ]
69 | ] [
70 | R.text [] "This is the home screen"
71 |
72 | R.touchableOpacity [
73 | OnPress(fun _ ->
74 | // push a new instance of the counter screen to the stack
75 | nav.navigation.push "counter"
76 | )
77 | ] [
78 | R.text [
79 | P.TextProperties.Style [
80 | P.FlexStyle.MarginTop (R.pct 5.)
81 | ]
82 | ] "Open counter screen"
83 | ]
84 | ]
85 |
86 | // the initial render function which creates the navigaiton container,
87 | // stack navigator and two stack screens
88 |
89 | // this function will initialize the available screens
90 | // React Navigation will push these to the stack and call the handler function (homePage and Counter.counter)
91 | let render () =
92 | navigationContainer [] [
93 | Stack.navigator [
94 | // tell React Navigation to open the home screen initially
95 | Stack.NavigatorProps.InitialRouteName "home"
96 | ] [
97 | Stack.screen "home" homePage [] []
98 | Stack.screen "counter" Counter.counter [
99 | // the counter screen expects a param with an initial counter value
100 | // default is nont
101 | Stack.ScreenProps.InitialParams ({Initial = None} : Counter.CounterProps)
102 | ] []
103 | ]
104 | ]
105 |
106 | // Update app name (react-native init )
107 | Helpers.registerApp "You app name" (render ())
108 | ```
109 |
110 | ## `Counter.fs`
111 |
112 | We will implement the classic counter sample, as demonstrated in the Elmish-based how-to, using function components and hooks instead of Elmish. Notice how we push a new counter screen with the double of the current counter value.
113 |
114 | ```fsharp
115 | module Counter
116 |
117 | open Fable.ReactNative.Navigation
118 |
119 | type CounterProps = {
120 | Initial : int option
121 | }
122 |
123 | type private Model = {
124 | // Store the navigation object in the model to be user for later
125 | Navigation : Types.INavigation
126 | Counter : int
127 | }
128 | and private Message =
129 | | Increment
130 | | Decrement
131 |
132 | let private init nav = {
133 | Navigation = nav
134 | Counter =
135 | // check the props given with the navigation object for a
136 | // initial counter value
137 | match nav.route.``params``.Initial with
138 | | None -> 0
139 | | Some i -> i
140 | }
141 |
142 | // standard update, but without the Cmd
143 | let private update model msg =
144 | match msg with
145 | | Increment -> {model with Counter = model.Counter + 1 }
146 | | Decrement -> {model with Counter = model.Counter - 1 }
147 |
148 | // a promise that will sleep for 2 sec before dispatching Increment
149 | let private delayedIncrement dispatch =
150 | promise {
151 | do! Promise.sleep 2000
152 | dispatch Increment
153 | }
154 |
155 | module R = Fable.ReactNative.Helpers
156 | module P = Fable.ReactNative.Props
157 | open Fable.ReactNative.Props
158 |
159 | let private buttonStyle : IStyle list = [
160 | P.Color "#fff"
161 | P.FlexStyle.MarginTop (R.pct 5.)
162 | P.FlexStyle.Padding (R.pct 2.)
163 | P.BorderWidth 1.
164 | P.BorderColor "#fff"
165 | ]
166 |
167 | // standard view
168 | let private view model dispatch =
169 | R.view [
170 | P.ViewProperties.Style [
171 | P.FlexStyle.Flex 1.0
172 | P.FlexStyle.JustifyContent JustifyContent.Center
173 | P.FlexStyle.AlignItems ItemAlignment.Center
174 | P.BackgroundColor "#131313"
175 | ]
176 | ] [
177 |
178 | // increment button
179 | R.touchableOpacity [
180 | OnPress(fun _ -> dispatch Increment)
181 | ] [
182 | R.text [P.TextProperties.Style buttonStyle] "Increment"
183 | ]
184 |
185 | //decrement button
186 | R.touchableOpacity [
187 | OnPress(fun _ -> dispatch Decrement)
188 | ] [
189 | R.text [P.TextProperties.Style buttonStyle ] "Decrement"
190 | ]
191 |
192 | // delayed increment button
193 | R.touchableOpacity [
194 | OnPress(fun _ ->
195 | // start promise
196 | delayedIncrement dispatch
197 | |> Promise.start
198 | )
199 | ] [
200 | R.text [P.TextProperties.Style buttonStyle] "Delayed Increment"
201 | ]
202 |
203 | // display current counter
204 | R.text [
205 | P.TextProperties.Style [
206 | P.Color "#ffffff"
207 | P.FontSize 30.
208 | P.TextAlign P.TextAlignment.Center
209 | P.FlexStyle.MarginTop (R.pct 4.)
210 | ]
211 | ] (string model.Counter)
212 |
213 | // button for pushing new counter screen
214 | R.touchableOpacity [
215 | OnPress (fun _ ->
216 | // the expected data type
217 | // push the current counter * 2 to the new screen
218 | let props = {Initial = Some (model.Counter * 2)}
219 |
220 | // helper function from Fable.ReactNative.Navigation
221 | // helps push new screens with data
222 | // pushed props to screen "counter" through nav obj from model
223 | pushWithData model.Navigation "counter" props
224 | )
225 | ] [
226 | R.text [
227 | P.TextProperties.Style buttonStyle
228 | ] "Open counter again with double current count"
229 | ]
230 | ]
231 |
232 |
233 | // create a function component with a reducer hook
234 | // the counter function receives the navigation object with props
235 |
236 | let counter (navigation : Types.INavigation ) =
237 |
238 | Fable.React.FunctionComponent.Of(fun (props : {| nav : Types.INavigation |} ) ->
239 | let initialModel = init props.nav
240 | // create a reducer hook
241 | let model = Fable.React.HookBindings.Hooks.useReducer(update, initialModel)
242 |
243 | view model.current model.update
244 | ) {|nav = navigation |}
245 | ```
246 |
247 | ## Links
248 | - [React Navigation](https://reactnavigation.org)
249 | - [Fable.ReactNative.Navigaion](https://github.com/martinmoec/Fable.ReactNative.Navigation)
250 | - [React Hooks with useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stockfora",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest",
10 | "lint": "eslint .",
11 | "build": "fable-splitter -c splitter.config.js --define RELEASE",
12 | "debug": "fable-splitter -c splitter.config.js --define DEBUG",
13 | "watch": "fable-splitter -c splitter.config.js -w --define DEBUG"
14 | },
15 | "dependencies": {
16 | "@react-native-community/netinfo": "^5.9.6",
17 | "buffer": "^6.0.3",
18 | "react": "17.0.2",
19 | "react-native": "0.64.2"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.14.8",
23 | "@babel/runtime": "^7.14.8",
24 | "@babel/preset-env": "^7.14.8",
25 | "@react-native-community/eslint-config": "^3.0.0",
26 | "babel-jest": "^27.0.6",
27 | "eslint": "^7.31.0",
28 | "jest": "^27.0.6",
29 | "metro-react-native-babel-preset": "^0.66.2",
30 | "react-test-renderer": "17.0.2"
31 | },
32 | "jest": {
33 | "preset": "react-native"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------