[React Native] Using variable as JSON’s key instead of hardcoded string

tl;dr

A relatively short blogpost sharing why I use variable as JSON’s key in React Native.

Introduction

As we all know that, JSON is key-value paired. See below code.

// A JSON
const json = {
  name: "Yik Kok",
  website: "www.yikkok.com"
}

Well, inside the curly brace, left hand side is the key and right hand side is the value.

Is that possible to replace the key with variable ? Yes, it is possible.

** But why ? (Well, I found it’s quite useful, I will explain my thought later)

Let see the code below.

const NAME = "name";
const WEBSITE = "website";

const json = {
  [NAME]: "Yik Kok",
  [WEBSITE]: "www.yikkok.com"
}

We can do it this way when using variable as the JSON’s key

So, what is it gonna do with React Native?

Well, this is because recently I’m figuring how to directly access specific styling from props in React Native – not by the style props, I know this could be done easily with just extend the props.

**(Yes, there’s tradeoff for this. So, I will limited the usage and apply to wherever make sense.)

Before I continue, let have a look for the demo video first.

It’s an ordinary updating text styling when the specific button was clicked. Under the scene, there is no if...else, no ? : (ternary statement) involved.

Let’s see the code.

import React, {useState} from 'react';
import {StyleSheet, Text, View, Button, SafeAreaView} from 'react-native';

const STATUS_NORMAL = 'Normal';
const STATUS_WARNING = 'Warning';
const STATUS_ERROR = 'Error';

const statusDescription = {
  [STATUS_NORMAL]: 'All Green.',
  [STATUS_WARNING]: 'Beware of Warning!',
  [STATUS_ERROR]: 'Error, Error!!!',
};
const App: () => React$Node = props => {
  const [status, setStatus] = useState(STATUS_NORMAL);

  function handleOnChangeStatus(newStatus) {
    return () => {
      setStatus(newStatus);
    };
  }

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles[status]}>{statusDescription[status]}</Text>

      <View>
        <Button
          title={STATUS_NORMAL}
          onPress={handleOnChangeStatus(STATUS_NORMAL)}
        />
        <Button
          title={STATUS_WARNING}
          onPress={handleOnChangeStatus(STATUS_WARNING)}
        />
        <Button
          title={STATUS_ERROR}
          onPress={handleOnChangeStatus(STATUS_ERROR)}
        />
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    padding: 20,
  },
  [STATUS_NORMAL]: {
    color: 'green',
  },
  [STATUS_WARNING]: {
    color: 'yellow',
    backgroundColor: 'black',
  },
  [STATUS_ERROR]: {
    color: 'red',
  },
});

export default App;

As we can see from the code. There is no if...else clause and no hardcoded string in the component tree. (I’m not saying that we should eliminate all hardcoded string from component tree. In some cases, it’s easy if you hardcoded it instead of using a variable)

Let’s get back to the code. Firstly, I have three variable indicating the status.

const STATUS_NORMAL = 'Normal';
const STATUS_WARNING = 'Warning';
const STATUS_ERROR = 'Error';

Then, I have two JSON – one is the statusDescription and another one is the styles that created using React Native’s StyleSheet API.

const statusDescription = {
  [STATUS_NORMAL]: 'All Green.',
  [STATUS_WARNING]: 'Beware of Warning!',
  [STATUS_ERROR]: 'Error, Error!!!',
};

and

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
    padding: 20,
  },
  [STATUS_NORMAL]: {
    color: 'green',
  },
  [STATUS_WARNING]: {
    color: 'yellow',
    backgroundColor: 'black',
  },
  [STATUS_ERROR]: {
    color: 'red',
  },
});

Now, the function – a closure that will pass the variables for update new status.

function handleOnChangeStatus(newStatus) {
  return () => {
    setStatus(newStatus);
  };
}

// e.g
<Button
  title={STATUS_NORMAL}
  onPress={handleOnChangeStatus(STATUS_NORMAL)}
/>

So, now whenever the status get updated, The content will updated accordingly to the references in JSON. For styling as well.

But why ?

Why put it in variable instead of string as the JSON key since both of them have the same purpose ? Hmmm, there is no specific rules to do it this way.

I found them useful to me is because of following reasons:

  1. Eslint plugins help me detect undefined variable but not string or JSON’s key.
    • Which allow me to do early fails detection.
  2. I’m a human, who will make mistake in my life, including typo… most of the time, this gonna take me a lot of time to debug or trace it.
  3. I heavy rely on the IntelliSense feature provided by Editor/IDE where they show me which variable match my need but not string.
  4. I’m lack of the ability to give everything a name and still able to recall them correctly, so put it in variable can help me prevent the duplication key issue.

Just some thought

This is inspire by a recent engineering blogpost from facebook. Where they utilise the SQLite to driven their UI. So, in my free time, I will try out experiment whether it could be done with the combination of JSON, variables and database to render UI in React Native.

Thank you for reading.  😁

Dialog and Custom React Hooks

tl;dr

We know that custom React Hooks allow us to reuse stateful logic. How can we utilize it with the Dialog component in Material UI ? Below, I will demonstrate the benefit of breaking component into individual pieces and utilize custom React Hooks to write cleaner React component.

Let’s Start

Install Material-UI before continue. As we will use the Dialog component from this library.

yarn add @material-ui/core

#1

Now, assume that we have one Dialog composed component base on Material-UI.

import React, { useState } from "react";

import { Container, Button, Typography, Dialog, DialogContent } from "@material-ui/core";

export default function App() {
  const [open, setOpen] = useState(false);

  function handleOnClose() {
    setOpen(false)
  }

  function handleOnOpen() {
    setOpen(true)
  }

  return <Container>
    <Typography component="div" align="center">
      <Button 
        variant="contained" 
        color="primary" 
        onClick={handleOnOpen}
      >
        Add
      </Button>
    </Typography>

    <Dialog open={open} onClose={handleOnClose}>
     {/* Please mock your content here */}
     <DialogContent />
    </Dialog>
  </Container>
}

#2

Now, let’s say, we are adding another Dialog. We humans are easier to get distracted. This apply to software development as well, as the codebase growth, it will add difficulty to debugging process. Because we are distracted by the others part of the code. So, how we going to keep our UI code as clean as possible ?

By separate the child component out of the parent. Let’s take a look, below code.

A Dialog component for adding new item.

import React from "react";
import { Dialog, DialogContent } from "@material-ui/core";

export default function DialogAddNewItem(props) {
  return <Dialog {...props}>
    { /* Please mock your content */ }
    <DialogContent />
  </Dialog>
}

Yet, we have another Dialog for editing existing item. We called it DialogEditItem.

import React from "react";
import { Dialog, DialogContent } from "@material-ui/core";

export default function DialogEditItem(props) {
  return <Dialog {...props}>
    { /* Please mock your content */ }
    <DialogContent />
  </Dialog>
}

This way we can add functionality to individual Dialog component without worrying that it would break each others. Even changing the UI.

So, we will re-write our App component as below.

import React from "react";

import DialogAddNewItem from "./DialogAddNewItem";
import DialogEditItem from "./DialogEditItem";

export default function App() {
  const [open, setOpen] = useState(false);
  const [openEditDialog, setOpenEditDialog] = useState(false);

  function handleOnClose() {
    setOpen(false)
  }

  function handleOnOpen() {
    setOpen(true)
  }

  function handleOnOpenEditDialog() {
    setOpenEditDialog(true)
  }

  function handleOnCloseEditDialog() {
    setOpenEditDialog(false)
  }

  return <Container>
    <Typography component="div" align="center">
      <Button 
        variant="outlined" 
        color="primary" 
        onClick={handleOnOpenEditDialog}
      >
        Edit
      </Button>
      <Button 
        variant="contained" 
        color="primary" 
        onClick={handleOnOpen}
      >
        Add
      </Button>
    </Typography>

    <DialogAddNewItem open={open} onClose={handleOnClose} />
    <DialogEditItem 
      open={openEditDialog} 
      onClose={handleOnCloseEditDialog} 
    />
  </Container>
}

It’s better, isn’t it ? Whenever we expand the functionality in DialogAddNewItem and DialogEditItem. It will not affect the codebase of App.

#3

Now, we having another issue. The state to control the Dialog component. Let’s say, we wanna add another Dialog. Then, we have to come out another new naming for the state. (I’m always lack of idea to think of new name)

Can we reuse the stateful logic ? Yes, we can. (Thanks React Hooks. You save me from thinking a new variable name)

import { useState } from "react";

export default function useDialog() {
  const [open, setOpen] = useState(false);

  function onOpen() {
    setOpen(true);
  }

  function onClose() {
    setOpen(false)
  }

  return { open, onOpen, onClose }
}

#4

Next, re-write our App component yet again.

import React from "react";

import DialogAddNewItem from "./DialogAddNewItem";
import DialogEditItem from "./DialogEditItem";
import useDialog from "./useDialog";

export default function App() {
  const dialogAddNewItem = useDialog();
  const dialogEditItem = useDialog();

  return <Container>
    <Typography component="div" align="center">
      <Button 
        variant="outlined" 
        color="primary" 
        onClick={dialogEditItem.onOpen}
      >
        Edit
      </Button>
      <Button 
        variant="contained" 
        color="primary" 
        onClick={dialogAddNewItem.onOpen}
      >
        Add
      </Button>
    </Typography>

    <DialogAddNewItem 
      open={dialogAddNewItem.open} 
      onClose={dialogAddNewItem.onClose} 
    />
    <DialogEditItem 
      open={dialogEditItem.open} 
      onClose={dialogEditIte.onClose} 
    />
  </Container>
}

Conclusion

This is one of the example to utilize custom React Hooks and write cleaner React component and not only limited to. The same concept can be apply to React Native as well.

Let’s write a better and cleaner React component with me. Thanks for reading. Have a nice day.  😁

React Hooks - useState and useEffect

React Hooks – useState and useEffect

tl;dr

Demo for react hooks usage – useState and useEffect. As I found out I haven’t write any post about the React Hooks yet.  😅

The first thing I look into React Hooks is the ability to reuse stateful logic (At that time, I haven’t familiar with the concept of Higher order Component yet.) Thus, I’m excited for the React Hooks where I found it can reduce a lot of repeated stateful logic.

Introduction

React Hooks was introduced in version 16.8. It allow you to interact with state in functional component.

#1 useState

@ Example 1

import React, { useState } from 'react';

function Example() {
  const [value, setValue] = useState(0);
}

@ Example 2

import React from 'react';

function Example() {
  const [value, setValue] = React.useState(0);
}

[value, setValue] – array destructuring in ES 6.

useState is return two element.

  • First element is the value of a state
  • Second element is the function to update the state
  • Naming convention doesn’t matter, but the order does

** How to swapping element using array destructuring ?

#2 useEffect

useEffect – a combination of componentDidMount, componentDidUpdate and componentWillUnmount.

@ Example

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    // synchronous callback
    windows.addEventListener();

    // cleanup, equivalent to componentWillUnmount
    return () => {
      windows.removeEventListener();
    }
  }, [dependencies])
  // dependencies will handle whether to re-run the useEffect or not
  // if dependencies value remain the same. it will skip useEffect
  // otherwise, execute the useEffect
}

@ Example – Execute once

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    // Equivalent to componentDidMount
    // Code, execute once on when the component mounted
  }, [])
}

@ Example – Execute every re-render

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    // Code, execute on every re-render
  })
}

@ Example – Execute only when dependencies updated

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    // Equivalent to componentDidUpdate
    // Execute if dependencies updated
    // Otherwise, skip useEffect
  }, [dependencies])
}

@ Example – Asynchronous in useEffect

import React, { useEffect } from 'react';

function Example() {
  // You are not allowed to do this, the callback is always synchronous
  useEffect(async () => {
    // Code
  }, [dependencies])
}

// you can try below
function Example() {
  useEffect(() => {
    async fetchSomething() {
      // Code
    }

    fetchSomething();
  })
}

#3 Yet another example

@ Example – Fetching blog posts

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

function MyBlog() {
  const [posts, setPosts] = useState([]);
  const [selectedCategories, setSelectedCategories] = useState(new Set([])); // Selected categories for filter
  const [page, setPage] = useState(0); // blogposts pagination
  const [loading, setLoading] = false;

  useEffect(() => {
    // Declare a function to fetch blogpost from WordPress API
    async fetchPosts(blogPage, blogCategory) {
      try {
        setLoading(true);
        const res = await fetch(
          `https://blog.yikkok.com/wp-json/wp/v2/posts?_embed&page=${blogPage}&categories=${blogCategory}&per_page=5`,
          {
            method: "GET"
          }
        ).then(response => response.json());

        if (res) {
          setPosts([...res]);
          setIsLoading(false);
        }
      } catch (error) {
        // handling error
        setIsLoading(false);
      }
    }
   
   const selectedCategoriesStr = Array.from(selectedCategories).join(",");
   fetchPosts(page, selectedCategoriesStr)
  }, [page, selectedCategories])
  // Because I'm using page and selectedCategories inside useEffect,
  // Thus, I pass the page and selectedCategories as the dependencies,
  // As I want the useEffect to re-run whenever either one updated

  function handleUpdatePage(pageNumber) {
    setPage(pageNumber);
  }

  function handleUpdateSelectedCategories(selected) {
    // A closure
    return () => {
      // Just for demo purpose, it may contain some bugs...
      const cloned = new Set(selectedCategories);

      if (cloned.has(selected)) {
        cloned.delete(selected)
      } else {
        cloned.add(selected)
      }

      setSelectedCategories(cloned);
    }
  }

  return (render UI)
}

#4 Custom React Hooks

Let’s write a custom React Hooks, Using useState and useEffect.

import React, { useState, useEffect } from 'react'

// React Hooks naming convention, always prefix with 'use'
export default function useBlogPosts() {
  const [posts, setPosts] = useState([]);
  const [selectedCategories, setSelectedCategories] = useState([]);
  const [page, setPage] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    async fetchPosts(blogPage, blogCategory) {
      try {
        setLoading(true);
        const res = await fetch(
          `https://blog.yikkok.com/wp-json/wp/v2/posts?_embed&page=${blogPage}&categories=${blogCategory}&per_page=5`,
          {
            method: "GET"
          }
        ).then(response => response.json());

        if (res) {
          setPosts([...res]);
          setLoading(false);
        }
      } catch (error) {
        // handling error
        setLoading(false);
        setError(error.message);
      }
    }
   
   const selectedCategoriesStr = Array.from(selectedCategories).join(",");
   fetchPosts(page, selectedCategoriesStr)
  }, [page, selectedCategories]);

  function handleUpdateSelectedCategories(selected) {
    const cloned = new Set(selectedCategories);

    if (cloned.has(selected)) {
      cloned.delete(selected)
    } else {
      cloned.add(selected)
    }

    setSelectedCategories(cloned);
  }

  function handleUpdatePage(pageNumber) {
    setPage(pageNumber);
  }

  return { 
    posts,
    loading,
    error,
    page,
    selectedCategories,
    handleUpdateSelectedCategories,
    handleUpdatePage
  }
}

#5 Use the custom React Hooks

// Refactor MyBlog Component
import React from 'react';
import useBlogPosts from 'src/hooks/useBlogPosts';

function MyBlog() {
  const blog = useBlogPosts();

  // blog.selectedCategories
  // blog.handleUpdatePage
  // blog.handleUpdateSelectedCategories
  // blog.error
  // blog.loading
  // blog.posts
  // blog.page
}

With custom React Hooks, we can separate the stateful logic out of UI component, which make us easier to debug the code without being distracted by the UI code.

Please visit my github commit, how I refactor the source code into custom React Hooks.

Takeaway

useState

  • Access and interact state in functional component

useEffect

  • Synchronous
  • Work similar to componentDidMount, componentDidUpdate and componentWillUnmount

Custom React Hooks – Reuse stateful logic.

Thanks for reading.  😁

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.  😁

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.   😁

Custom React Hooks

Reusable Custom React Hooks

tl;dr

Create a custom React Hooks that can be share among ReactJS and React Native codebase.

A custom React Hooks for functional component. The purpose is more or less similar to my previous post Reusable HoC – Part 1 and Resuable HoC – Part 2.

This post is assuming you have a basic understanding about React Hooks. As this post will not explain what is React Hooks.

Outline

Let’s start

Step 1 – Use React Hooks in functional component

Let’s start, create a file and give it a name called demo.js or whatever you like.

import React, { useState, useEffect } from "react";
import { FlatList } from "react-native";
import { Container, Content } from "native-base";
import NewsItemWithAPIUsingHooks from "../components/NewsItemWithAPIUsingHooks";

export default function DemoReactHooks() {
  const [topStory, setTopStory] = useState([]);

  useEffect(() => {
    async function fetchTopStories() {
      try {
        const res = await fetch(`https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty`, {
          method: "GET",
        }).then(res => res.json());

        if (res) {
          setTopStory([...res]);
        }
      } catch (error) {
        // handling error
      }
    }

    fetchTopStories();
  }, []);

  function renderStory({ item, }: { item: Object }) {
    return (
      <NewsItemWithAPIUsingHooks storyID={item} />
    );
  }

  return (
    <Container>
      <Content>
        <FlatList data={topStory} keyExtractor={(item, index) => `${item}-${index}`} renderItem={renderStory} />
      </Content>
    </Container>
  );
}

As the code show above, we fetch the topstory news at useEffect which is when the component mounted. Then, display the result – a list of story Id in the <FlatList />.

What if we want to reuse the same stateful logic somewhere else or maybe sharing the same codebase with React Native ?

  • Copy paste
  • Custom React Hooks

Step 2 – Extracts React Hooks into custom React Hooks

Well, let’s extract current structure into a custom React Hooks for reuse purpose.

Now, create a new file called useStory.js and copy paste below code into the file.

import { useState, useEffect } from "react";

export default function useStory(storyType: string) {
  const [response, setResponse] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);

  useEffect(() => {
    async function fetchStory(storyType: string) {
      try {
        setIsLoading(true);
        const res = await fetch(`https://hacker-news.firebaseio.com/v0/${storyType}.json?print=pretty`, { method: "GET" }).then(res => res.json());

        if (res) {
          setResponse(res);
          setIsLoading(false);
        }
      } catch (error) {
        // handling error
        setHasError(true);
      }
    }

    fetchStory(storyType);

    return () => {
      // componentWillUnmount, cancel API request or event listener
      // To prevent memory leaked
    };
  }, [storyType]);

  return {
    response,
    isLoading,
    hasError
  };
}

Now, we extract the stateful logic into a custom Hooks. The naming convention of custom React Hooks should prefix with use. There is a eslint plugins to help developer linting the naming of custom React Hooks.

useEffect – fetching API request and return a function that will be execute when the component unmount. Cancel an API request or remove event listener will be done inside return function.

Access to the state ? Return the state at the end of the custom React Hooks.

Step 3 – Refactor component to use custom React Hooks

import React from "react";
import { FlatList } from "react-native";
import { Container, Content } from "native-base";
import NewsItemWithAPIUsingHooks from "../components/NewsItemWithAPIUsingHooks";
import useStory from "../hooks/useStory";

export default function DemoReactHooks() {
  const topstories = useStory("topstories");

  function renderStory({ item, }: { item: Object }) {
    return (
      <NewsItemWithAPIUsingHooks storyID={item} />
    );
  }

  return (
    <Container>
      <Content>
        <FlatList data={topstories.response} keyExtractor={(item, index) => `${item}-${index}`} renderItem={renderStory} />
      </Content>
    </Container>
  );
}

Now, it’s much more cleaner than before. useStory React Hooks is able to reuse in other component even in React Native without copy paste.

Conclusion

Finally, come to an end of the series of creating reusable stateful business logic, to share codebase among web and mobile app (ReactJS and React Native).

To maximize the reusability of HoC or custom React Hooks, it’s better to keep it as simple as possible and not coupling too much third party JavaScript library.

A reusable HoC or custom React Hooks is good for Junior developer or someone who is new to a project’s codebase or team. This way can reduce the time to study and get familiar with the codebase and make them onboard as soon as possible. They can be more focus on the input and output.

Maintenance issue. Now, fixed a bug in business logic only done once instead of twice or more.

Separation of concern. Separate business logic out of the presentation view. Easier for unit testing and update UI especially when the component grow larger

Thanks for reading. Full source code can be obtain from my github repository

Higher Order Component - Part 2

Higher order Component in React Native – Part 2

tl;dr

In my previous post, I explain about write a HoC, encourage more reuse of codebase. This post I will try to explain more example, to make a HoC more flexible (certain use case).

Dynamic Props

import React, { Component } from "react";

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

      this.state = {
        stories: []
      }
    }
    
    componentDidCatch() {
      // error handling
    }

    async componentDidMount() {
      try {
        const res = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty', { method: "GET" }).then(res => res.json());

        if (res) {
          this.setState({ stories: res })
        }
      } catch (error) {
        // error handling
      }
    }

    render() {
      const { stories } = this.state;
      return <WrappedComponent {...this.props} stories={stories} />
    }
  }
}

Given example above from my previous post. We facing an issue as following:

  • It’s only allow developer to fetch topstories resource from HackerNews API. (may not be a bad case, sometimes)
// latest item from HackerNews API
https://hacker-news.firebaseio.com/v0/maxitem.json?print=pretty

// Up to 500 top stories / new stories / best stories
https://hacker-news.firebaseio.com/v0/[topstories|newstories|beststories].json?print=pretty

// Up to 200 Ask / Job / Show stories
https://hacker-news.firebaseio.com/v0/[askstories|showstories|jobstories].json?print=pretty

It’s okay that if you build each API endpoint a standalone HoC (For single responsibility pattern or maybe due to business logic is vary). What if you can reuse the same HoC even for some combination too.

Below is an example, make it as dynamic as possible (Please prioritize maintaining rather than dynamic use case, as in most of the time the most dynamic function is the one hardest to maintain and debug, trade off)

import React, { Component } from "react";

export default function withStories(endpoint, dynamicProps) {
  return (WrappedComponent) => {
    return class extends Component {
      constructor(props) {
        super();

        this.state = {
          stories: []
        }
      }
    
      componentDidCatch() {
        // error handling
      }

      async componentDidMount() {
        try {
          const res = await fetch(endpoint, { method: "GET" }).then(res => res.json());

          if (res) {
            this.setState({ stories: res })
          }
        } catch (error) {
          // error handling
        }
      }

      render() {
        const { stories } = this.state;
        return <WrappedComponent 
                {...this.props} 
                {...{ [dynamicProps]: stories }} />
      }
    }
  }
}

Since, HoC is a JavaScript closures. We can modify it to take in and holding additional parameter – endpoint and dynamicProps.

This way a developer can passing any endpoint (Let’s limited to certain purpose or use case, otherwise it will caused the HoC hard to maintain at the end).

Then, the dynamicProps define how a developer accessing the the props. dynamicProps is defined by developer, for two purpose.

  • The business logic / HoC is encapsulated. Developer more focus on input and output. Less worry for handling business logic.
  • To prevent props collision, when it’s come to combine two or more HoC
export default function withStories(endpoint, dynamicProps) {
  return (WrappedComponent) => {
    return class extends React.Component {
      ...
      render() {
        return <WrappedComponent {...{ [dynamicProps]: value }} />
      }
    }
  }
}

Combines two or more HoC

One of the way, to combine multiple HoC as below:

withHoCOne(withHoCTwo(withHoCThree(App)));

Well, it solve the combine multiple HoC issue. At the same time, it create another issue – readability. Yes, the code is hard to read.

Here, recommend some utility to help solving the issue and maintain the code readability.

compose – from several package

This way we can combine multiple HoC without sacrifice the code readability.

compose(
  withHoCOne,
  withHoCTwo,
  withHoCThree,
)(App)

Beware of

Redundant props or some unused props. In ReactJS, a component is re-render when the props changed. So, if a HoC passing some redundant props and the value had changed it will caused the component to re-render. A bug that is hard to trace.

Conclusion

Wisely use of HoC is promoting the reuse of codebase. Improve maintainability and component unit testability.

Thanks for reading.  😁

Higher Order Component

Higher order Component in React Native – Part 1

tl;dr

Wrapped API in Higher order Component to promote reusable component and static component unit testing

Scenario

Create a component, fetching API/resources in componentDidMount. Most of the tutorial, start this way. This is the easier way for anyone to learn fetching API/resources in ReactJS/React Native. I don’t against this pattern. But, things go complicated in real world business. Fetching multiple API or reuse the same business logic (stateful business logic).

Copy paste might solve your problem and create duplicated code or even worse the codebase will grow very fast. What if you can done the job with a function instead of copy paste ? This way you can separate the business logic and presentation component, making component smaller and modular. It will be a good investment in long terms (reduce tech debt).

Wrapped API/resources fetching or business logic in Higher order Component promote the reusable of presentation component and business logic.

** A plain Higher order Component without too much dependencies allow you to share the same code with ReactJS for web or vice versa.

// Example

export default class App extends React.Components {
  componentDidMount() {
    fetch();
    fetch();
  }

  render() {
    return <View>
      {resources}
      {resources}
    </View>
  }
}

How to utilize Higher order Component (HoC) ?

*From here onward, will calling HackerNews API for demo purpose*

Let’s start with fetching topstories from HackerNews API – return a list of story ID. Then, fetching another API for particular story details.

// list of story ids
https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty

// details of story
https://hacker-news.firebaseio.com/v0/item/121003.json?print=pretty

First, let create the first Higher order Component (HoC), for topstories from HackerNews API.

import React, { Component } from "react";

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

      this.state = {
        stories: []
      }
    }
    
    componentDidCatch() {
      // error handling
    }

    async componentDidMount() {
      try {
        const res = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty', { method: "GET" }).then(res => res.json());

        if (res) {
          this.setState({ stories: res })
        }
      } catch (error) {
        // error handling
      }
    }

    render() {
      const { stories } = this.state;
      return <WrappedComponent {...this.props} stories={stories} />
    }
  }
}

In Higher order Component, component was parameterizedWrappedComponent. In above example, the HoC can now be reuse to any component that need to access the topstories resources.

The withTopStories will fetching the topstories resources from HackerNews API and set to the state – stories. Later on, passing the resources as a props to the parameterized component – WrappedComponent.

Accessing the injected props

Now, we want to access the injected props. Maybe a <FlatList />

import React, { Component } from "react";
import { FlatList, Text } from "react-native";
import withTopStories from "path/to/withTopStories";

class App extends Component {
  renderStories({ item, index }) {
    return <Text>{item}</Text>
  }

  render() {
    const { stories } = this.props;
    return <FlatList 
            data={stories} 
            renderItem={this.renderStories} 
            keyExtractor={(item, index) => `${item}-${index}`} 
           />
  }
}

export default withTopStories(App);

Then, we will render a list of number which is the story’s ID from HackerNews API. In order to get the details, we need the ID to fetch another API.

Yet another HoC for another endpoint

Then, we create another HoC for fetching the details of story by the ID.

import React, { Component } from "react";

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

      this.state = {
        details: {}
      }
    }

    async componentDidMount() {
      try {
        const { storyID } = this.props;
        const details = await fetch(`https://hacker-news.firebaseio.com/v0/item/${storyID}.json?print=pretty`, { method: "GET" }).then(res => res.json());

        if (details) {
          this.setState({ details })
        }
      } catch (error) {
        // error handling
      }
    }

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

Now, we need a component to display the details. Let’s create one. This time we won’t wrapped it directly, instead we will create a static version. This allow us easy to mock the data and unit testing the component.

import React from "react";
import { Text } from "react-native"

export default function Story(props) {
  const { details, ...rest } = props;
  return <Text {...rest}>{details.title}</Text>
}
/**
 * Version 2.0
 */
import React, { Component } from "react";
import { FlatList } from "react-native";
import withTopStories from "path/to/withTopStories";
import withStoryDetails from "path/to/withStoryDetails";
import Story from "path/to/Story"; // the component we create above

const WrappedStory = withStoryDetails(props => {
  const { storyID, ...rest } = props;
  return <Story storyID={storyID} {...rest} />
});

class App extends Component {
  renderStories({ item, index }) {
    // replace the below component 
    return <WrappedStory storyID={item} />
  }

  render() {
    const { stories } = this.props;
    return <FlatList 
             data={stories} 
             renderItem={this.renderStories} 
             keyExtractor={(item, index) => `${item}-${index}`} 
           />
  }
}

export default withTopStories(App);

Closing

Now, both React Native and ReactJS can share the HoC. Use it on mobile or web. Wrapped it with any presentation component without duplicate code. Easier to unit testing static component.

Functional and Class Component

ReactJS – Functional and Class Components

tl;dr

ReactJS是一个由脸书(Facebook)在2013年所发布的UI library. ReactJS鼓吹以零件式(component-based) 的架构, 创建网站的前端. 可以想象成建构前端就像玩乐高(Lego)一样, 你所见到的按键(buttons), 链接(links), 页面(page), 输入(input), 截面(section), 段落(paragraph)或图像(images)等等, 都可以变成一个个独立的小零件, 然后在把它们进行组装. 以达到界面重用(Reusable UI component).

接下来, 是给想入手ReactJS的一些个人看法. 基本要求:
* HTML, CSS, JavaScript (ES 6)

#1 Component

Class Component

import React, { Component } from "react"

Class Sample extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isActive: false
    }

    // 重点, 如果,不bind.当你click那个button,就会有error.
    // 不信, 你可以试试 😂
    this.handleToggleStatus.bind(this)
  }

  handleToggleStatus() {
    this.setState({
      isActive: !this.state.isActive
    })
  }

  render() {
    return (
      <>
        <button 
          onClick={this.handleToggleStatus} 
          style={{ backgroundColor: this.props.buttonColor || "#ffffff" }}
         >
           {this.state.isActive ? "Activated" : "Deactivated" }
        </button>

        // 这会是同样的一个按键,不过backgroundColor是红色的
        <Sample buttonColor={this.state.isActive ? "#ff0000" : "#ffffff" } />
      </>
    )
  }
}

如上所示, 每按一下按键(button), 字眼Activated和Deactivated就会跳动更换.

class component 也可以叫stateful component. 在class component里头你可以通过setState, 以更改component的state(状态). 每一个setState都会重新执行render(). 界面(UI)就会更新.

除了state, props的更新也会刷新界面(UI). 在上面的示范, 按键的backgroundColor会因为this.props.buttonColor的不同而改变.

Class component可以运用ReactJS的component’s lifecycle

一个component的state, 可以是其他component的props.

Tips 1: 为什么要bind ?
Tips 2: ReactJS 的Class/Stateful component的概念与Flutter的Stateful Widget是一样的.
Tips 3: <>...</>这个syntax是React Fragment的shorthand

Functional Component

import React from "react";

function Sample(props) {
  return <button {...props}>...</button>
}

在ReactJS 16.8版本之前, functional component又称Stateless component. 因为你只能通过props刷新界面(UI). 没有statesetState.

在ReactJS 16.8版本之后, 就有了React Hooks. React Hooks缩小了class component和functional component之间的差别. 简而言之, 你可以在functional component里头, 使用setState的概念. 如下,

import React, { useState } from "react"

function Sample(props) {
  // 顺序很重要, [value, function]
  // 第一个, 是value
  // 第二个, 是function
  const [isActive, setIsActive] = useState(false);

  function handleToggleStatus() {
    setIsActive(prevState => !prevState)
  }

  return (
    <button onClick={handleToggleStatus} >
      {isActive ? "Activated" : "Deactivated"}
    </button>
  )
}

See the Pen Functional Component and Hooks by Yik Kok (@yikkok-yong) on CodePen.

Tips 4: Functional/Stateless component的概念和Flutter的Stateless Widget是一样的.
Tips 5: Functional component里面不会需要this, 自然也不需要bind
Tips 6: React Hooks目前还未能完全取代class component

结语

Functional component 和 class component都有各自的好. 没有绝对. 视情况而使用. (只有更好, 没有最好)