Callback JavaScript

Callback JavaScript

tl;dr

What’s callback function in JavaScript ? Callback function in plain JavaScript, Asynchronous and React

So, what is callback function ?

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

MDN Web Docs

Above is the definition of callback function from MDN Web Docs.

# Real life example ?

Scenario:

  1. Bob give a call to Alice.
  2. Alice isn’t pick up the call.
  3. Thus, Bob leave a message to Alice, to get back to Bob as soon as possible.
  4. Alice receive the message and give a call/message to Bob.

When Bob leave a message to Alice, we can imagine that Bob is passing a callback function and awaiting Alice responded to the callback function.

Callback function in plain JavaScript

/**
 * A function taking two params
 * @param {string} message - A string output to console
 * @param {function} reply - A function (callback function)
 */
function calls(message, reply) {
  console.log(message)
  reply()
}

/**
 * Output string to console
 */
function replyFromAlice() {
  console.log("I'm Alice, speaking please.");
}

function replyFromRobot() {
  console.log("The number you have dials is unreachable. Please leaves a message")
}

calls("May I speak to Alice ?", replyFromAlice);
// Expected from console.log
// May I speak to Alice ?
// I'm Alice, speaking please.

calls("Baby Shark Du...Du...Du...", replyFromRobot);
// Expected from console.log
// Baby Shark Du...Du...Du...
// The number you have dial is unreachable. Please leave a message

Asynchronous callback function

/**
 * Asynchronous callback function
 * Return "...\newline Hello ?" after awaiting 3seconds
 */
function asyncCalls(message) {
  return new Promise(resolve => { 
    console.log(message); 
    setTimeout(() => { 
      resolve("...\n Hello ?");
    }, 3000);
  });
}

/**
 * Asynchronous function, `then` and `catch` accessible from promise
 * resolve - will fall in `then` clause
 * reject - will fall in `catch` clause (I don't include `reject` in this post)
 */
asyncCalls("Du...Du...Du...").then(function(response) {
  // now, this is a callback function for `resolve()`
  console.log(response)
  // Expected
  // ...
  // Hello ?
}).catch(() => {})

// ES 6 - arrow function pattern(Don't be confused, both is doing the same thing)
// asyncCalls("Du...Du...Du...").then(response => {
//   console.log(response)
// }).catch(() => {})

then() clause is accepting a function as parameter. We can access the value return from resolve(value). For the case above, we are return the ...\n Hello ? in the resolve(). Hence, we can access the value in then().

Using callback in React

Pass the callback function as a props and get it fired whenever you need it.

  • Passing data from child component(s) to parent component(s)

@ Passing data from child component(s) to parent component(s)

import React from "react";

function Child(props) {
  function handleOnClick() {
    // Business logic

    const { onClickChild } = props;

    if (onClickChild) {
      // Fire the callback function to return value if needed.
      onClickChild("Demo for passing data to parent component");
    }
  }

  return <button onClick={handleOnClick}>Child</button>
}

export default function Parent() {
  function handleOnClickChild(valueFromChild) {
    console.log(valueFromChild);
    // Expected result
    // "Demo for passing data to parent" in console
  }

  return <Child onClickChild={handleOnClickChild} />;
}

On Child component, the onClick event on <button /> is accepting a callback function. Thus, we pass in a callback function handleOnClick().

Next, the onClickChild prop is another callback function for <Child /> component. It provide the functionality for parent component to retrieve the value from <Child /> or to continue action in parent component without intercept the default business logic in <Child />.

Conclusion

It’s good to know the fundamental of callback because it’s useful tools to handle asynchronous action or something you need to do-it-later.

** (Google about callback-hell. Async/await is come to rescue to prevent callback-hell (for promise) )

Thanks for reading.  😁

React Navigation 5.0.0

React Navigation Version 5.0

tl;dr

Updated on 18 March 2020

Giving a try on React Navigation version 5.0 – declarative way to implement navigation in React Native.

Let’s start

#1 Prerequisites

@ Let’s start

Generate a new project using react-native cli.

$ react-native init TryReactNavigationV5

or

$ react-native init TryReactNavigationV5 --version [version-number]

@ Install React Navigation

** yarn or npm, depend on your project package management.

Install react-navigation using following command. Take note that now react-navigation use the new package-name.

$ yarn add @react-navigation/native
$ yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

@ Install React Navigation Stack

Starting React Navigation version 4. The StackNavigator, DrawerNavigator BottomTabNavigator or etc has been isolated as standalone package. Thus, in order to use specific Navigator, one must install from npm.

  • createStackNavigator
$ yarn add @react-navigation/stack
  • createDrawerNavigator
$ yarn add @react-navigation/drawer
  • createBottomTabNavigator
$ yarn add @react-navigation/bottom-tabs
  • createMaterialBottomTabNavigator
$ yarn add @react-navigation/material-bottom-tabs react-native-paper react-native-vector-icons

react-native-vector-icons installation guide.

  • createMaterialTopTabNavigator
$ yarn add @react-navigation/material-top-tabs react-native-tab-view
  • iOS (Installed require library using Cocoapods)
cd ios && pod install && cd ..

To complete the installation of react-native-gesture-handler.

Import react-native-gesture-handler in the entry file – App.js or index.js depend on your file structure. The first line of file before anything else.

import 'react-native-gesture-handler';

...

@ Error from react-native-reanimated

You may probably hitting similar error, after installing the react-native-reanimated and try to run react-native run-android.

This is due to react-native-reanimated library required an extra properties – supportLibVersion in projectRootFolder/android/build.gradle.

Solution,

ext {
    buildToolsVersion = "28.0.3"
    minSdkVersion = 16
    compileSdkVersion = 28
    targetSdkVersion = 28
    supportLibVersion = "28.0.0" // Add this line to resolve the error
}

@ React Navigation – declarative way

// App.js
import React, { useState } from "react";
import {
  View,
  TextInput,
  Text,
  Button,
  StyleSheet,
  ActivityIndicator,
} from "react-native";
import { NavigationProp } from "@react-navigation/core";
import { NavigationNativeContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";

const Stack = createStackNavigator();

const App: () => React$Node = () => {
  const [token, setToken] = useState("");
  const [loading, setLoading] = useState(false);

  if (loading) {
    return (
      <View style={style.container}>
        <ActivityIndicator size="large" />
      </View>
    );
  }

  return (
    <NavigationNativeContainer>
      <Stack.Navigator initialRouteName={token ? "Home" : "Login"}>
        <Stack.Screen
          name="Login"
          component={Login}
          options={{ header: null }}
        />
        <Stack.Screen name="Home" component={Home} />
      </Stack.Navigator>
    </NavigationNativeContainer>
  );
};

const style = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  inputContainer: {
    width: "80%",
    marginBottom: 20,
  },
  input: {
    borderStyle: "solid",
    borderWidth: 1,
  },
  label: {
    fontWeight: "bold",
    paddingVertical: 10,
  },
});

export default App;

<Stack.Navigator /> and <Stack.Screen /> The new declarative way to implement navigation in React Native.

if (loading) {
  return (
    <View style={style.container}>
      <ActivityIndicator size="large" />
    </View>
  );
}

Above code is a loading screen. In the mean while, getting the token from AsyncStorage.

Has error due to missing of Login and Home component ? Now, we will resolve the error. Add the Login and Home screen component respectively.

// In App.js too
import AsyncStorage from "@react-native-community/async-storage";

type Props = {
  navigation: NavigationProp,
};
function Login(props: Props) {
  return (
    <View style={style.container}>
      <View style={style.inputContainer}>
        <Text style={style.label}>Username</Text>
        <TextInput placeholder="Username" style={style.input} />
      </View>

      <View style={style.inputContainer}>
        <Text style={style.label}>Password</Text>
        <TextInput placeholder="******" secureTextEntry style={style.input} />
      </View>
      <Button
        title="Login"
        onPress={async () => {
          const { navigation } = props;

          // testing purpose
          await AsyncStorage.setItem("token", "yourtoken");
          navigation.navigate("Home");
        }}
      />
    </View>
  );
}

function Home() {
  return (
    <View style={style.container}>
      <Text>Home Page</Text>
    </View>
  );
}

Install AsyncStorage from react-native-community/async-storage.

Then, some modification to App component as below.

// App.js

const App: () => $ReactNode = () => {
  // code
  
  useEffect(() => {
    async function init() {
      setLoading(true);
      const userToken = await AsyncStorage.getItem("token");

      if (userToken) {
        setToken(userToken);
      }

      setLoading(false);
    }

    init();
  }, []);

  // return
  return (
    // Code
  )
}

Using the React Hooks – useEffect(). As initialize step, to fetch the token from AsyncStorage.

If the token exists or valid, depend on architecture. Then, we can decide the initialRouteName, either to Home or Login.

<Stack.Navigator initialRouteName={token ? "Home" : "Login"}>

Thanks for reading.  😁

Here is the full gists.

AWS CodeCommit

Setup git repository using AWS CodeCommit service

tl;dr

Git – an important tool in software developer’s job scope/daily life. One of the skill that employers will look for in candidate profile (at least in my country – Malaysia). Most of the software developer will store the source code in cloud services, such as GitHub, BitBucket, GitLab, self-hosted git repository or etc. AWS CodeCommit is one of the service where we can store our source code in AWS.

Let’s start

#1 Introduction to AWS CodeCommit

  • Belongs to “Always free” category
    • First 5 active users
    • 50GB storage per month
    • 10, 000 git requests per month
  • AWS CodeCommit pricing

#2 Setup your first repository in AWS CodeCommit

AWS CodeCommit

Navigate to CodeCommit from the AWS Console.

AWS CodeCommit repository dashboard

Click “Create repository”

Create repository in AWS CodeCcommit

Take note that Repository names are restricted to alphanumeric characters, plus ‘.’, ‘_’, and ‘-‘. No space allowed in repository name.

#3 Setup SSH connection to AWS CodeCommit

Steps to setup SSH connection in AWS CodeCommit

Generate public/private key pair from terminal. (Please use GitBash or Windows Subsystem Linux(WSL) for Windows platform)

AWS provided clear instructions regarding how to generate ssh key for:

$ ssh-keygen
For illustration purpose

By default, ssh private and public key will store as /home/username/.ssh/id_rsa and /home/username/.ssh/id_rsa.pub respectively. You may want to name it something else to prevent override your default.

$ cat ~/.ssh/id_rsa.pub
For illustration purpose

Copy the output, where you will need to paste it in AWS CodeCommit.

Upload SSH public key to CodeCommit Credentials. First, click the account at the top navigation bar and click the “My Security Credentials”.

Click My Security Credential

Navigate to AWS CodeCommit credentials tab.

AWS CodeCommit credentials

Upload SSH public key.

Copy and paste the generated ssh public key into the field

After uploaded SSH public key, AWS will generated a SSH Key ID which acts as git user to access AWS CodeCommit service. Copy the SSH Key ID.

Uploaded SSH public key

Now, modify the ssh config file /home/username/.ssh/config. If you don’t have the config file, feel free generate one using your favourite terminal editor (vi, nano or etc).

Host git-codecommit.*.amazonaws.com
  User Your-SSH-Key-ID
  IdentityFile /path/to/your/private key
For illustration purpose

#4 Testing SSH connection

$ ssh git-codecommit.ap-southeast-1.amazonaws.com

If all the setup is correct, we will get the success message in terminal as below.

For illustration purpose

#5 Git and AWS CodeCommit

Clone from AWS CodeCommit

$ git clone ssh://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/personal-website

Add AWS CodeCommit as remote

$ git remote add aws ssh://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/personal-website

Push to AWS CodeCommit

$ git push aws

or

$ git push origin

#6 View Repository in AWS CodeCommit

Source code push from local machine

Thanks for reading.  😁

Online and Offline events in ReactJS

Online and offline events in ReactJS

A web apps that work both online and offline (or a slow network connection) has become relatively important in our life. Glad that browser provide related API for such event, online and offline events listener. How to do it in ReactJS using Hooks or HoC ?

#1 React Hooks

Below is a simple React Hooks for detect browser online and offline events.

import React, { useState, useEffect } from "react";

export default function useOnlineStatus() {
  const [online, setOnline] = useState(true);

  useEffect(() => {
    window.addEventListener("online", handler);
    window.addEventListener("offline", handler);

    // cleanup
    return () => {
      window.removeEventListener("online", handler);
      window.removeEventListener("offline", handler);
    }
  });

  function handler(event) {
    setOnline(navigator.OnLine);

    if (event.type === "online") {
      // handle stuffs when browser resume online
    } else {
      // handle stuffs when browser goes offline
    }
  }

  return { online }
}

@ Example – Use it in UI Component

Prerequisite

import useOnlineStatus from "useOnlineStatus";
import SnackBar from "@material-ui/core/SnackBar";

function Demo() {
  const status = useOnlineStatus();

  return (
    <>
      <SnackBar 
        open={!status.online} 
        anchorOrigin={{ vertical: 'top', horizontal: 'center' }} />
      <p>{status.online ? "Online" : "Offline" }</p>
    </>
  )
}

#2 Higher order Component

import React, { Component } from "react";

export default function withOnlineStatus(WrappedComponent) {
  return class extends Component {
    constructor() {
      super();

      this.state = {
        online: true
      }
    }

    componentDidMount() {
      window.addEventListener("online", this.handler)
      window.addEventListener("offline", this.handler)
    }

    componentWillUnmount() {
      window.removeEventListener("online", this.handler)
      window.removeEventListener("offline", this.handler)
    }

    handler = (event) => {
      this.setState({ online: Navigator.OnLine })

      if (event.type === "online") {
        // handle stuffs when browser resume online
      } else {
        // handle stuffs when browser goes offline
      }
    }

    render() {
      const { online, ...rest } = this.props;
      return <WrappedComponent online={online} {...rest} />
    }
  }
}

@ Example – Use it in UI Component

Prerequisite

  • Material-UI – SnackBar
import SnackBar from "@material-ui/core/SnackBar";
import withOnlineStatus from "withOnlineStatus";

function Demo(props) {
  const { online } = props;

  return (
    <>
      <SnackBar 
        open={!online} 
        anchorOrigin={{ vertical: 'top', horizontal: 'center' }} />
      <p>{online ? "Online" : "Offline" }</p>
    </>
  )
}

export default withOnlineStatus(Demo)

Thanks for reading.   😁