- 1. First React Native project
- 2. Tab navigation
- 3. Stack navigation nested in the tab navigator
- 4. Creating your first component
- 5. Packaging modes and debugging
- Extra: Styling
- Lab 4 Solution project
- Once you accepted this assignment, clone your repository to your local environment.
- Open VS Code, open a new terminal.
- Create a new empty react-native project by running:
expo init IISSI2-IS-Frontend-Owner
- Choose template:
blank
- Choose template:
- Navigate to the created folder:
cd IISSI2-IS-Frontend-Owner
and run:npm start
- A new tab/window on your browser should open http://localhost:19002/ showing Expo developers tools. If not automatically opened, run your browser and navigate to: http://localhost:19002
- (On windows) set allow on Firewall/Windows defender if asked
- Click on
Run in web browser
. It should open the basic empty project in a new tab. It should look like this: https://snack.expo.dev/@afdez/lab4-1-blankproject - Configure eslint by installing and initialize it for your project by running
npm install eslint --save-dev
andnpx eslint --init
. We recommend the following answers: To check syntax, find problems, and enforce code style, JavaScript modules (import/export), React, Does your project use TypeScript? › No, Where does your code run? Browser, Use a popular style guide, Standard: https://github.com/standard/standard, Format-> Javascript. Finally select yes to install all dependencies. If you are asked to do some downgrade, answer yes. See https://eslint.org/docs/user-guide/getting-started for more details. - Modify eslint file in order to add the last rule.
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'plugin:react/recommended',
'standard'
],
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 12,
sourceType: 'module'
},
plugins: [
'react'
],
rules: {
'react/prop-types': 'off'
}
}
- After that it is recommended to include a settings file by running
mkdir .vscode
andtouch .vscode/settings.json
. Add the following contents at.vscode/settings.json
:
{
"eslint.lintTask.enable": true,
"eslint.alwaysShowStatus": true,
"eslint.format.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": ["javascript"]
}
Remember that, for a better experience, use the VSCode Extension for eslint: https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
- Sync and push your changes to your repository.
According to the requirements, we can classify owners functionalities in the following categories:
- Profile (login, logout and edit his profile)
- Restaurants (listing owned restaurants, create and edit them), navigate to its orders as well.
- Control panel showing business stats
We propose to create a Tab navigation using Reactnavigation tools, and following these steps (detailed documentation can be found at: https://reactnavigation.org/docs/tab-based-navigation)
-
Install react navigation dependencies by running
expo install react-native-screens react-native-safe-area-context
andnpm install @react-navigation/bottom-tabs
. We will also install stack navigator and other dependencies for the future by runningnpm install @react-navigation/native-stack
andnpm install @react-navigation/native
-
Create folders and files:
- Create a new folder
src\screens
- Create a folder for each tab
src\screens\profile
,src\screens\restaurants
,src\screens\controlPanel
- For each folder we will create a simple screen,
ProfileScreen.js
,RestaurantsScreen.js
andControlPanelScreen.js
you can create these folders and files using the terminal by running:
mkdir src && mkdir src/screens && mkdir src/screens/profile && mkdir src/screens/restaurants && mkdir src/screens/controlPanel && touch src/screens/profile/ProfileScreen.js && touch src/screens/restaurants/RestaurantsScreen.js && touch src/screens/controlPanel/ControlPanelScreen.js
Depending on your terminal/shell, you could need to change
&&
to;
- Create a new folder
-
Include a minimal screen for each screen file created. E.g. RestaurantsScreen.js
import React from 'react'
import { View, Text } from 'react-native'
export default function RestaurantsScreen () {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Restaurants</Text>
</View>
)
}
- Overwrite the contents of
App.js
the logic to create a tab navigator
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { NavigationContainer } from '@react-navigation/native'
import * as React from 'react'
import ControlPanelScreen from './src/screens/controlPanel/ControlPanelScreen'
import ProfileScreen from './src/screens/profile/ProfileScreen'
import RestaurantsScreen from './src/screens/restaurants/RestaurantsScreen'
const Tab = createBottomTabNavigator()
export default function App () {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Restaurants"
component={RestaurantsScreen} />
<Tab.Screen
name="Control Panel"
component={ControlPanelScreen} />
<Tab.Screen
name="Profile"
component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
)
}
- Check the new version of the project, it should works like this: https://snack.expo.dev/@afdez/lab4-2-basicnavigation
- Commit and push your changes to your repository. Open Source control tab in VSCode to commit and push your project.
- Additionally you can create a tag
Git: Create tag
, name it (e.g.lab4-2-basicNavigation
), and finallyGit: Push tags
- Additionally you can create a tag
Some screens should present a stack-type navigation. We are going to follow the steps below to include a stack navigator within the Restaurants tab, we will have a listing of all restaurants, and if you click on one, it will navigate to show the detail of that restaurant. The following video shows a stack navigator:
stack.mov
- Notice that we previously installed stack package
npm install @react-navigation/native-stack
andnpm install @react-navigation/native
- Create a second screen
RestaurantDetailScreen.js
atsrc/screens/restaurants
and include basic contents as before - Create a file
RestaurantsStack.js
insrc/screens/restaurants
and include the logic to define two stack screens forRestaurantsScreen
andRestaurantDetailScreen
.
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import React from 'react'
import RestaurantDetailScreen from './RestaurantDetailScreen'
import RestaurantsScreen from './RestaurantsScreen'
const Stack = createNativeStackNavigator()
export default function RestaurantsStack () {
return (
<Stack.Navigator>
<Stack.Screen
name='RestaurantsScreen'
component={RestaurantsScreen} />
<Stack.Screen
name='RestaurantDetailScreen'
component={RestaurantDetailScreen} />
</Stack.Navigator>
)
}
- Update App.js in order to include the
RestaurantStack
as the component for the corresponding Tab and update the import
<Tab.Screen
name="Restaurants"
component={RestaurantsStack} />
You can now refresh your app and check that the stack is rendered, but no way of navigating from one screen to the other is defined.
- In order to navigate from
RestaurantsScreen
toRestaurantDetailScreen
we need to receive the navigation object and create a button for the action. Moreover, we can pass data from one screen to another including a second parameter:
/* eslint-disable react/prop-types */
import React from 'react'
import { Button, Text, View } from 'react-native'
export default function RestaurantsScreen ({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Random Restaurant</Text>
<Button
onPress={() => {
navigation.navigate('RestaurantDetailScreen', { id: Math.floor(Math.random() * 100) })
}}
title="Go to Random Restaurant Details"
/>
</View>
)
}
- We can access data passed through the route object at
RestaurantDetailScreen.js
/* eslint-disable react/prop-types */
import React from 'react'
import { View, Text } from 'react-native'
export default function RestaurantDetailScreen ({ route }) {
const { id } = route.params
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Restaurant details. Id: {id}</Text>
</View>
)
}
- Check the new version of the project, it should works like this: https://snack.expo.dev/@afdez/lab4-3-nestednavigation
- You can commit and push your changes and optionally include a tag
Let's create a simple component which shows system information a if the app is running on development or production mode.
- Create a folder
src/components
and a new fileSystemInfo.js
which shows the information about the platform (web, iOS, Android), version and if the app is running in production or development mode. Notice that in production apps runs faster due to several compiling and packaging optimizations.
/* eslint-disable react/prop-types */
/* eslint-disable no-undef */
import React from 'react'
import { Platform, Text, View } from 'react-native'
export default function SystemInfo () {
return (
<View >
<Text>Platform: {Platform.OS}.</Text>
<Text>{Platform.Version ? `Version: ${Platform.Version}` : null}</Text>
<Text>Mode: {__DEV__ ? 'Development' : 'Production'}</Text>
</View>
)
}
- Use it in some screens by importing and including the component on your views.
- Try from different platforms by running some simulator or scanning the QR code with your mobile. Note: Your mobile has to be in the same network as the development server.
- Check the new version of the project, it should works like this: https://snack.expo.dev/@afdez/lab4-4-firstcomponent
- (optional) Publish your project using the publish option at the left
- You can commit and push your changes and optionally include a tag
- Start a debugging session on the web server version (Open inspector in the browser) and include a breakpoint at
RestaurantDetailScreen.js
at lineconst { id } = route.params
. Use the menus to inspect the values of variables and step over to next lines. - Do the same with the mobile version of the app. Open developer menu and activate Debug remote JS option. It will open a new tab on your browser. Open Inspect tools and look for a debuggerworker in the sources tab->pages
- Change to production mode by using the switch at the left side of the development console on http://localhost:19002/. It is possible you have to stop (ctrl-c on the terminal) and start the server again
npm start
when you change from development to production mode and viceversa.
- You can include to
App.js
some icons for the bottom tab options and remove default header (stack navigator shows its own header)
import { MaterialCommunityIcons } from '@expo/vector-icons'
<Tab.Navigator screenOptions={({ route }) => ({
// eslint-disable-next-line react/display-name
tabBarIcon: ({ color, size }) => {
let iconName
if (route.name === 'Restaurants') {
iconName = 'silverware-fork-knife'
} else if (route.name === 'Control Panel') {
iconName = 'view-dashboard'
} else if (route.name === 'Profile') {
iconName = 'account-circle'
}
return <MaterialCommunityIcons name={iconName} color={color} size={size} />
},
headerShown: false
})}>
- Include custom titles for each screen of the Restaurants Stack
<Stack.Navigator>
<Stack.Screen
name='RestaurantsScreen'
component={RestaurantsScreen}
options={{
title: 'Restaurants'
}}/>
<Stack.Screen
name='RestaurantDetailScreen'
component={RestaurantDetailScreen}
options={{
title: 'Restaurant Detail'
}}/>
</Stack.Navigator>
- Define a
styles\GlobalStyles.js
file including some constants for some colors named after:brandPrimary
,brandSecondary
,background
, etc.
const brandPrimary = '#be0f2e' // Granate US. rgba(190,15,46,255)
const brandPrimaryTap = '#AA001A' // Granate US más oscuro
const brandSecondary = '#feca1b' // Amarillo US.rgba(254,202,27,255)
const brandSecondaryTap = '#EAB607' // amarillo US más oscuro
const brandSuccess = '#95be05' // verde US
const brandBackground = 'rgb(242, 242, 242)' // gris claro
export { brandPrimary, brandPrimaryTap, brandSecondary, brandSecondaryTap, brandSuccess, brandBackground }
- Tip: you can use a VSCode extension to highlight hex, rgba color codos. E.g: https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight
- Use your branding colors, e.g. define a theme at
GlobalStyles.js
for ReactNavigation. See: https://reactnavigation.org/docs/themes/
const navigationTheme = {
dark: false,
colors: {
primary: brandSecondary,
background: brandBackground,
card: brandPrimary,
text: '#ffffff',
border: `${brandPrimary}99`,
notification: `${brandSecondaryTap}ff` // badge
}
}
- Remember that you can include styles at the end of each component by creating a StyleSheet. E.g:
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})