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

Remove duplicated element

How to remove duplicate elements in JavaScript ?

tl;dr

[Update on 2 Dec 2019]

Ways to remove duplicated elements in JavaScript

  • Array.prototype.filter()
  • JSON
  • Set

#1 Using Array.prototype.filter()

const duplicated = [
  "John",
  "Johnny",
  "Jonathan",
  "Christine",
  "Conan",
  "Christine",
  "Johnny"
];

duplicated.filter(
  (element, index) => duplicated.indexOf(element) === index
)

// Expected result
// [ 'John', 'Johnny', 'Jonathan', 'Christine', 'Conan' ]

#2 JSON

const duplicated = [
  "John",
  "Johnny",
  "Jonathan",
  "Christine",
  "Conan",
  "Christine",
  "Johnny"
];

const unique = {};

duplicated.forEach(val => {
  unique[val] = val;
});

console.log(Object.values(unique));
// or console.log(Object.keys(unique))
// Expected result
// [ 'John', 'Johnny', 'Jonathan', 'Christine', 'Conan' ]

#3 Set

const duplicated = [
  "John",
  "Johnny",
  "Jonathan",
  "Christine",
  "Conan",
  "Christine",
  "Johnny"
];

// first, how to convert Array to Set
const setFromArray = new Set([]);

// second, how to convert Set back to Array
const arrayFromSet = [...new Set([])];

// So, in order to remove duplicate elements using Set
// first, convert the Array to Set
// Then, convert back to Array
console.log([...new Set(duplicated)]

// or console.log(Object.keys(unique))
// Expected result
// [ 'John', 'Johnny', 'Jonathan', 'Christine', 'Conan' ]