Navigation Bar

By Fangtai Dong
Picture of the author
Published on
Navigation Bar

The Importance of React Navigation for Developers

  • As a developer, it is important to stay on top of the latest technologies and tools to ensure that your projects are efficient and effective. One such tool that has gained immense popularity in recent years is React Navigation, which is used to create efficient and aesthetically pleasing navigation systems in React Native applications. In this blog post, we will discuss why developers need to use React Navigation and explore some of its key features and benefits.

    1. Simplifies Navigation Setup:
    • One of the main reasons why developers need to use React Navigation is that it simplifies navigation setup. With React Navigation, developers can easily create a navigation system with minimal configuration and setup. This means that developers can focus more on building other important features of their application without worrying about complicated navigation setups.
    2. Better User Experience:
    • Another important benefit of using React Navigation is that it enables developers to create better user experiences. React Navigation provides various customizable options to create visually appealing navigation systems that seamlessly integrate with the overall interface of the application. This enhances the user experience and ensures that the navigation system is both intuitive and easy to use.
    3. Cross-Platform Compatibility:
    • React Navigation is also useful because it allows for cross-platform compatibility. This means that the navigation system created with React Navigation can be used in both iOS and Android applications, which simplifies the development process. Developers do not need to create two different navigation systems for each platform, which saves a considerable amount of time and effort.
    4. Supports Different Navigation Types:
    • React Navigation also supports different types of navigation, including stack, tab, and drawer navigation. This allows developers to create the most suitable navigation system depending on the specific needs of their application. This flexibility ensures that the navigation system is both efficient and tailored to the specific requirements of the application.
    5. Robust Documentation and Community Support:
    • Lastly, React Navigation has robust documentation that serves as a valuable resource for developers who want to learn more about how to use this tool. In addition, there is a supportive community of developers who are willing to offer guidance and support to those who are just starting to use React Navigation. This means that developers do not have to navigate the learning process alone and can leverage the expertise of others in the community.

React Navigation

1. Using React Navigation
2. Focusing on Tab Navigation
  • Possibly the most common style of navigation in mobile apps is tab-based navigation. This can be tabs on the bottom of the screen or on the top below the header (or even instead of a header).
  • tab navigation
3. Minimal example of tab-based navigation
  • import * as React from 'react'
    import { Text, View } from 'react-native'
    import { NavigationContainer } from '@react-navigation/native'
    import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
    
    function HomeScreen() {
      return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <Text>Home!</Text>
        </View>
      )
    }
    
    function SettingsScreen() {
      return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <Text>Settings!</Text>
        </View>
      )
    }
    
    const Tab = createBottomTabNavigator()
    
    export default function App() {
      return (
        <NavigationContainer>
          <Tab.Navigator>
            <Tab.Screen name="Home" component={HomeScreen} />
            <Tab.Screen name="Settings" component={SettingsScreen} />
          </Tab.Navigator>
        </NavigationContainer>
      )
    }
    
4. Customizing the appearance

Mock Server

  • Data transmission: CLOUD --> services.js --> context.js --> application
  • Services Layer: Service itself stems to determine what methods we want in order to get data from external (external services provider e.g: Google)

Promise

  • const myPromise = new Promise((resolve, reject) => {
      resolve('success')
    })
    
  • myPromise.then((res) => console.log('my promise', res))
    
  • const myFetch = fetcth('external url')
      // transfer JSON to JS object
      .then((response) => response.json())
      // print JS object
      .then((json) => console.log(json))
    

Camelize

  • yarn add camelize

Spread Operator

  • In JavaScript, the spread operator... It is mainly used to expand the contents of an array or object into another array or object. However, it can only handle one layer of depth, that is to say, it will not recurs to nested arrays or objects. This means that for nested structures, the extension operator can only "expand" the outer structure.

  • let nestedObject = {
      a: 1,
      b: {
        c: 2,
        d: {
          e: 3,
        },
      },
    }
    
    let shallowCopy = { ...nestedObject }
    
    shallowCopy.b === nestedObject.b // true
    shallowCopy.b.d === nestedObject.b.d // true
    
  • Although shallowCopy looks the same as the original object, its property b is a shallow copy of the original internal object. This means that nestedObject.b and shallowCopy.b actually refer to the same object in memory.

  • For deeper replication, you need to use other methods, such as using recursive functions for deep replication, or using existing library functions (such as Lodash's _.cloneDeep) to handle the replication and deconstruction of deep structures.


Events Context

  • By getting data from serviesAPI and delivery it to all levels of the application

  • import { useState, createContext, useEffect, useMemo } from 'react'
    
    // import mock request and transformed events data
    import { eventsRequest, eventsTransform } from './eventsService'
    
  • // globally use Events Context
    export const EventsContext = createContext()
    
    // 1. create Provider to wrap the app
    // 2. and provide the certain states
    // 3. provide initial value
    export const EventsContextProvider = ({ children }) => {
      return <EventsContext.Provider value={{ events: [1, 2, 3] }}>{children}</EventsContext.Provider>
    }
    
  • // use Context value in global component
    import { EventsContext } from '/services'
    
    import { useContext } from 'react'
    
    const eventsContext = useContext(EventsContext)
    
    eventsContext.events // [1,2,3]
    
  • Follow the Logic of React ContextAPI:

    1. In Context.js file, create Context and ContextProvider
    2. In App.js file, wrap all components with ContextProvider
    3. In useingContext.js file, use context by useContext hook with Context that imported from step 2
  • About Step 3, the Mechanism is:

    • When useContext(EventsContext) is called in a subcomponent, what happens here is:

    1. React will traverse the component tree up until it finds<EventsContext.Provider> component.

    2. Then, it will use the value attribute of the Provider component, which is the object { events: [1, 2, 3] } you pass.

    3. The useContext hook will return this object, so the eventsContext variable you get in the component is now a reference to this object.


useState & useEffect

  • Using state in Context.js file (useState)

  • Hook them up to be controlled (useEffect)

  • Once the component is mounted, I retrieve the data

    • useEffect(() => {
        retrieveRestaurants()
      }, [])
      
    • const retrieveEvents = () => {
        // set loading
        setIsLoading(true)
      
        // mimic a slow network
        setTimeout(() => {
          // events request has default location
          eventsRequest()
            .then(eventsTransform)
            .then((events) => {
              setIsLoading(false)
              setEvents(events)
            })
            .catch((err) => {
              setIsLoading(false)
              setError(err)
            })
        }, 2000)
      }
      

Loading Indicator

  • Use react-native-paper
  • Search for ActivityIndicator

Search Feature

  • ...

Geocoding in Google

  • setting up Geocoding service

  • address words mutually converted longitude and latitude

  • How to convert logic into code?
    1. In service folder, add location folder

    • Location Mock (mimic Geo location data)
    • Location Service (send request + transform)
    • Location Context (pass location value to whole app)
      • pass search function and defualt keyword

    1. Create a Search Component (for View)

    • Get Context from locationContext
    • Destructuring search function and keyword
    • set keyword state
    • return search component
      • <SearchContainer>
          <Searchbar
            placeholder="Search for a location"
            value={searchKeyword}
            onSubmitEditing={() => {
              search(searchKeyword)
            }}
            onChangeText={(text) => {
              setSearchKeyword(text)
            }}
          />
        </SearchContainer>
        
    1. onChangeText:
    • When the content in the text box (i.e. user input) changes, the onChangeText callback function will be triggered.

    • This function receives a parameter, that is, the current value of the text box (the content entered by the user).

    • Whenever the user changes the text input, onChangeText will use the current text to call a function to update the state (possibly a React state), which is the setSearchKeyword function. This means that the searchKeyword status will be updated to the text in the search bar in real time.

    • In this way, the application can capture every change in text input and perform other logic accordingly (for example, real-time search suggestions, form verification, etc.).

    1. onSubmitEditing:
    • This callback function is triggered when "submit" edits, usually when the user presses the "Search" or "Return" button on the soft keyboard.

    • When the user completes the input and submits (usually pressing the "Enter" or "Return" key on the virtual keyboard), the executed function does not accept the value of the input box as a parameter. Instead, it takes advantage of the value previously captured by onChangeText (i.e. searchKeyword status).

    • When the user submits what they enter in the search bar, onSubmitEditing will call the search function and pass in the value currently stored in the searchKeyword. Based on this value, the search function may perform search queries or trigger other search-related operations.


Inadvertently introduce some anti-pattern?

  • Check and reflect the code that I have written

  • Avoid infinite loop caused by state and context

  • Example: in Search component

    • export const Search = () => {
        const { keyword, search } = useContext(LocationContext)
        const [searchKeyword, setSearchKeyword] = useState(keyword)
      
        // cause infinite loop
        useEffect(() => {
          search(searchKeyword)
        }, [])
      
        return <SearchContainer>//...</SearchContainer>
      }
      
  • Solution: put useEffect in Context file so that it can make Context more intelligent

  • onSearch passed out as a function, so that receive searchKeyword can be used to update keyword state and useEffect watch keyword change, and then depend whether re-render or not!

     export const LocationContextProvider = ({ children }) => {
       const [keyword, setKeyword] = useState('')
       const [location, setLocation] = useState(null)
       const [isLoading, setIsLoading] = useState(false)
       const [error, setError] = useState(null)
    
       <!-- IMP -->
       const onSearch = (searchKeyword) => {
         setIsLoading(true)
         setKeyword(searchKeyword)
       }
    
       useEffect(() => {
         if (!keyword.length) {
           // don't do anything
           return
         }
         locationRequest(keyword.toLowerCase())
           .then(locationTransform)
           .then((result) => {
             setIsLoading(false)
             setLocation(result)
             console.log(result)
           })
           .catch((err) => {
             setIsLoading(false)
             setError(err)
             console.log(err)
           })
       }, [keyword])
    
       return (
         <LocationContext.Provider
           value={{
             isLoading,
             error,
             location,
    
             <!-- !!!IMP -->
             search: onSearch,
             keyword,
           }}
         >
           {children}
         </LocationContext.Provider>
       )
     }
    

Stack Navigation

  1. Restructuring Navigation
  2. Install the package: yarn add @react-navigation/stack
  3. Install the package: yarn add react-native-gesture-handler
  4. Splitting Navigation to navigation folder index file, and then in this file export appNavigator with Tab.Screen contained stack navigation of EventsNavigator
  5. navigation prop in EventStack.Screen
  • <EventStack.Navigator>
      <EventStack.Screen component={EventsScreen}></EventStack.Screen>
    </EventStack.Navigator>
    
  • export const EventsScreen = ({navigation}) => {//...}
    
  1. SreenOption configuration
  2. Setting up Event Detail Navigation
  3. Pressable vs TouchableOpacity
  4. TransitionPresets imported from @react-navigation/stack
  • <RestaurantStack.Navigator
      headerMode="none"
      screenOptions={{
        ...TransitionPresets.ModalPresentationIOS,
      }}
    >
    
  1. route prop in EventsScreen
  • return (
      <Eventstack.Navigator screenOptions={createScreenOptions}>
        <Eventstack.Screen name="Events" component={EventsScreen} />
        <Eventstack.Screen name="EventDetail" component={EventDetailScreen} />
      </Eventstack.Navigator>
    )
    
  • export const EventDetailScreen = ({ route }) => {
      // console.log(route.params);
      // get the route(EventDatil) payload through route.parms
      const { event } = route.params
      return (
        <SafeArea>
          <EventInfo event={event} />
        </SafeArea>
      )
    }
    
  1. Stack Navigation Summary
    Stack Navigation Logic
    Stack Navigation Logic

Accordion group

  1. List.Accordion & List.Icon
  2. List.Item
  3. Scrollable

Hi there! Want to support my work?

© 2023 Fangtai Dong. All rights reserved.