<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ridho C Pamungkas</title>
    <description>The latest articles on Forem by Ridho C Pamungkas (@ridhopamungkas).</description>
    <link>https://forem.com/ridhopamungkas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F786866%2Fc5a88e2a-e61e-4312-b8d0-04099c385970.png</url>
      <title>Forem: Ridho C Pamungkas</title>
      <link>https://forem.com/ridhopamungkas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ridhopamungkas"/>
    <language>en</language>
    <item>
      <title>Custom image handler React-Quill</title>
      <dc:creator>Ridho C Pamungkas</dc:creator>
      <pubDate>Sat, 31 Dec 2022 09:24:34 +0000</pubDate>
      <link>https://forem.com/ridhopamungkas/custom-image-handler-pada-react-quill-39g3</link>
      <guid>https://forem.com/ridhopamungkas/custom-image-handler-pada-react-quill-39g3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0oa7pbjs0lw1087rc6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0oa7pbjs0lw1087rc6y.png" alt="Image description" width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
Pada kali ini saya akan sharing cara mengubah handler module image bawaan dari react-quill. Karena handler module image react-quill mengahasilkan data base64, yang mana datanya akan panjang sendiri untuk 1 image dan membuat memory membengkak saat data html tagnya akan disimpan ke database. Dan disini saya akan mengubah alur upload image dengan mengirim imagenya ke upload service yang disediakan BE untuk disimpan ke dalam local file system dan mengahasilkan response url string.&lt;/p&gt;

&lt;p&gt;Kita mulai dengan menginstall react-quill&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i react-quill
npm i quill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Berikut sample react-quill menggunakan handlers defaultnya&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ReactQuill from "react-quill";
import Quill from "quill";
import "react-quill/dist/quill.snow.css";

const RichContentEditor = () =&amp;gt; {
    const modules = React.useMemo(
    () =&amp;gt; ({
      toolbar: {
        container: [
          [{ font: [] }],
          [{ header: [2, 3, false] }],
          [{ align: [] }],
          ["bold", "italic", "underline"],
          [{ list: "ordered" }, { list: "bullet" }],
          ["link", "image", "video"],
          ["clean"],
        ],
      },
      history: {
        delay: 500,
        maxStack: 100,
        userOnly: true,
      },
    }),
    []
  );

return (
    &amp;lt;&amp;gt;
      &amp;lt;ReactQuill
        id={"richContent"}
        modules={modules}
        formats={[
          "header",
          "font",
          "bold",
          "italic",
          "underline",
          "list",
          "bullet",
          "align",
          "link",
          "image",
          "video",
        ]}
        placeholder={""}
        onChange={(e) =&amp;gt; setContentValue(e)}
        value={contentValue}
        theme="snow"
      /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;dibawah ini contoh yang sudah menggunakan custom image handlersnya, untuk fungsi-fungsinya saya cantumkan dalam comment saja, saya hanya menjelaskan custom image handlers disini untuk membuka modal yang berisikan upload image dan form input property image yang dibutuhkan untuk dikirimkan ke upload service dan meng-embed url imagenya.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useRef, useState } from "react";
import ReactQuill from "react-quill";
import { Button, Form, Input, Modal } from "tunggal";
import { UploadPreview } from "pages/product/detail/detail.styles";
import Quill from "quill";
import "react-quill/dist/quill.snow.css";
import IPayloadAPI from "interfaces/iapi";
import upload from "libraries/api/upload";
import { callAPI } from "utils/fetchData";
import { errorNotification, formatBytes } from "utils/commonFunction";
import { ListStyled } from "pages/product/variant/variant.styles";
import { initialImage } from "helpers/constants";
import { default as UploadDrag } from "rc-upload";
import ImageBlot from "./image-blot";

const RichContentEditor = () =&amp;gt; {
  let reactQuillRef = useRef(null); // ReactQuill component
  let quillRef = useRef(null); // Quill instance

  const [showModalImage, setShowModalImage] = useState(false);
  const [contentValue, setContentValue] = useState("");
  const [isFetchingUpload, setIsFetchingUpload] = useState(false);
  const [titleImage, setTitleImage] = useState("");
  const [altImage, setAltImage] = useState("");
  const [selectedImage, setSelectedImage] = useState(initialImage);

  // show / close modal
  const handleShowModal = () =&amp;gt; {
    setShowModalImage(!showModalImage);
    handleReset();
  };

  const handleReset = () =&amp;gt; {
    setAltImage("");
    setTitleImage("");
    setSelectedImage(initialImage);
  };

  // function onchange property image
  const onChangeInput = (key: string, e: string) =&amp;gt; {
    const valueRegex = e.replace(/[^\w\s]/gi, "");
    if (key === "title") {
      setTitleImage(valueRegex);
    } else {
      setAltImage(valueRegex);
    }
  };

  // simpan fileImage ke dalam state
  const onUpload = async (e) =&amp;gt; {
    const file = e;
    if (file) {
      setSelectedImage({
        image: file,
        size: file.size,
        fileName: file.name,
      });
    }
    e.target.value = null;
  };

  // function cek fokus index terakhir element input react-quill
  const handleSetQuillRef = () =&amp;gt; {
    if (typeof reactQuillRef.current.getEditor !== "function") {
      return;
    }
    const temp = reactQuillRef.current.getEditor();
    quillRef.current = temp;
  };

  // function Request ke upload Service yang menghasilkan url image
  const uploadImage = async () =&amp;gt; {
    if (selectedImage.image) {
      handleSetQuillRef();

      const range = quillRef.current.getSelection(true);
      const titles = titleImage ? titleImage : selectedImage.fileName;

      const fileData = new FormData();
      fileData.append("file", selectedImage.image);
      fileData.append("type", "richcontent");
      fileData.append("uploadType", "image");
      fileData.append("filename", titles);
      let params: IPayloadAPI = upload.uploadFile(fileData);
      setIsFetchingUpload(true);
      try {
        const response = await callAPI(params);
        const { data } = response.data;

        // proses mengembed image pada range index terakhir
        quillRef.current.insertEmbed(
          range.index,
          "image",
          {
            alt: altImage, // untuk memasukkan property ALT image
            title: titles, // untuk memasukkan property title image
            url: data.original, // untuk memasukkan properti src image
          },
          "user"
        );

        set fokus element input lompat 2 index agar tidak bertabrakan dengan image yang sudah di embed
        quillRef.current.setSelection(range.index + 2, "silent");
        quillRef.current.focus();
        handleShowModal();
      } catch (error) {
        errorNotification(`Upload File`, error);
      }
    }
  };

  // Penggunaan useMemo disini untuk memoize hasil function custom handlers yang dipanggil berulang kali agar tidak bentrok

  const modules = React.useMemo(
    () =&amp;gt; ({
      toolbar: {
        container: [
          [{ font: [] }],
          [{ header: [2, 3, false] }],
          [{ align: [] }],
          ["bold", "italic", "underline"],
          [{ list: "ordered" }, { list: "bullet" }],
          ["link", "image", "video"],
          ["clean"],
        ],
      },
      history: {
        delay: 500,
        maxStack: 100,
        userOnly: true,
      },
      handlers: {
        image: handleShowModal, // customs handlers image untuk memanggil function handleShowModal
      },
    }),
    []
  );
  // daftarkan Quill instance module image
  Quill.register({ "formats/imageBlot": ImageBlot });

  return (
    &amp;lt;&amp;gt;
      &amp;lt;ReactQuill
        id={"richContent"}
        modules={modules}
        formats={[
          "header",
          "font",
          "bold",
          "italic",
          "underline",
          "list",
          "bullet",
          "align",
          "link",
          "image",
          "video",
        ]}
        placeholder={""}
        onChange={(e) =&amp;gt; setContentValue(e)}
        value={contentValue}
        theme="snow"
      /&amp;gt;
      &amp;lt;Modal
        size="sm"
        show={showModalImage}
        onClose={handleShowModal}
        title="Tambah Image"
        variant="clean"
      &amp;gt;
        &amp;lt;Modal.Body&amp;gt;
          &amp;lt;Form&amp;gt;
            &amp;lt;Form.Item
              label="Upload Image"
              extra={
                &amp;lt;&amp;gt;
                  Atau seret file ke area di atas &amp;lt;br /&amp;gt;
                  Format .jpg or .jpeg
                &amp;lt;/&amp;gt;
              }
            &amp;gt;
              &amp;lt;UploadPreview&amp;gt;
                &amp;lt;UploadDrag
                  name="upload"
                  beforeUpload={(e) =&amp;gt; onUpload(e)}
                  accept="image/jpeg"
                  type="drag"
                &amp;gt;
                  &amp;lt;Button icon="plus"&amp;gt;Upload Image&amp;lt;/Button&amp;gt;
                &amp;lt;/UploadDrag&amp;gt;
              &amp;lt;/UploadPreview&amp;gt;
              {selectedImage.image &amp;amp;&amp;amp; (
                &amp;lt;ListStyled isModal&amp;gt;
                  &amp;lt;li&amp;gt;
                    &amp;lt;div&amp;gt;
                      &amp;lt;strong&amp;gt;{selectedImage.fileName}&amp;lt;/strong&amp;gt;
                      &amp;lt;span&amp;gt;{formatBytes(selectedImage.size)}&amp;lt;/span&amp;gt;
                    &amp;lt;/div&amp;gt;

                    &amp;lt;Button
                      iconOnly
                      icon="trash"
                      onClick={() =&amp;gt; setSelectedImage(initialImage)}
                    /&amp;gt;
                  &amp;lt;/li&amp;gt;
                &amp;lt;/ListStyled&amp;gt;
              )}
            &amp;lt;/Form.Item&amp;gt;
            &amp;lt;Form.Item label="Image Title"&amp;gt;
              &amp;lt;Input
                placeholder="Input Image Title"
                value={titleImage}
                onChange={(e) =&amp;gt; onChangeInput("title", e)}
              /&amp;gt;
            &amp;lt;/Form.Item&amp;gt;
            &amp;lt;Form.Item label="Image ALT Title"&amp;gt;
              &amp;lt;Input
                placeholder="Input Image ALT Title"
                value={altImage}
                onChange={(e) =&amp;gt; onChangeInput("alt", e)}
              /&amp;gt;
            &amp;lt;/Form.Item&amp;gt;
          &amp;lt;/Form&amp;gt;
        &amp;lt;/Modal.Body&amp;gt;
        &amp;lt;Modal.Footer&amp;gt;
          &amp;lt;Button disabled={isFetchingUpload} onClick={handleShowModal}&amp;gt;
            Cancel
          &amp;lt;/Button&amp;gt;
          &amp;lt;Button
            color="primary"
            loading={isFetchingUpload}
            onClick={uploadImage}
          &amp;gt;
            Upload
          &amp;lt;/Button&amp;gt;
        &amp;lt;/Modal.Footer&amp;gt;
      &amp;lt;/Modal&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default RichContentEditor;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Buat File image.blot.tsx untuk mendaftarkan custom property image (alt, title, src)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Quill from 'quill';

const BlockEmbed = Quill.import('formats/image');

class ImageBlot extends BlockEmbed {
  static create(val) {
    const node = super.create();
    node.setAttribute('alt', val.alt);
    node.setAttribute('title', val.title);
    node.setAttribute('src', val.url);
    return node;
  }

  static value(node) {
    return {
      alt: node.getAttribute('alt'),
      title: node.getAttribute('title'),
      url: node.getAttribute('src'),
    };
  }
}
ImageBlot.blotName = 'image';
ImageBlot.tagName = 'img';

export default ImageBlot;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;seperti ini output yang dihasilkan &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8mj5w75cpmuig4lqigj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8mj5w75cpmuig4lqigj.png" alt="Image description" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Membuat debounce function</title>
      <dc:creator>Ridho C Pamungkas</dc:creator>
      <pubDate>Fri, 09 Sep 2022 08:08:24 +0000</pubDate>
      <link>https://forem.com/ridhopamungkas/membuat-debounce-function-p7j</link>
      <guid>https://forem.com/ridhopamungkas/membuat-debounce-function-p7j</guid>
      <description>&lt;p&gt;Kasus dibawah ini melakukan efisiensi pada live search data, dengan menggunakan debounce function ini kita bisa mendebounce karakter yang diinput user yang berubah dengan cepat dan mengeksekusinya saat waktu yang kita tentukan. dengan melakukan ini kode tidak melakukan pencarian setiap karakter berubah, ini membuat lebih efisien dan tidak mengganggu performance saat melakukan request ke suatu API.&lt;/p&gt;

&lt;h2&gt;
  
  
  function useDebounce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useEffect } from "react";

const useDebounce = (val: any, delay: number) =&amp;gt; {
  const [debounceVal, setDebounceVal] = useState(val);

  useEffect(() =&amp;gt; {
    const handler = setTimeout(() =&amp;gt; {
      setDebounceVal(val);
    }, delay);

    return () =&amp;gt; {
      clearTimeout(handler);
    };
  }, [val]);

  return debounceVal;
};

export default useDebounce;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contoh sederhana
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState } from "react";
import useDebounce from "./use-debounce";

const listData = ["sabun", "minyak", "gula", "garam", "kopi"];

function App() {
  const [results, setResults] = useState&amp;lt;string[]&amp;gt;([]);
  const [text, setText] = useState("");

  const debounce = useDebounce(text, 500);

  useEffect(() =&amp;gt; {
    const filterData = listData.filter((obj) =&amp;gt;
      obj.toLowerCase().includes(debounce)
    );
    setResults(filterData);
  }, [debounce]);

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;input
        type="text"
        value={text}
        onChange={(e) =&amp;gt; setText(e.target.value)}
      /&amp;gt;
      {results.length &amp;gt; 0 ? (
        results.map((el: string, i: number) =&amp;gt; &amp;lt;div key={i}&amp;gt;{el}&amp;lt;/div&amp;gt;)
      ) : (
        &amp;lt;div&amp;gt;no results&amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contoh menggunakan fetching data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState } from "react";
import useDebounce from "./use-debounce";
import useFetch from "./use-fetch";


function App() {
  const [text, setText] = useState("");

  const debounce = useDebounce(text, 500);
  const url = `http://www.omdbapi.com/?t=${debounce}`;
  const {data, isLoading, error} = useFetch(url);

  if(isLoading){
    return &amp;lt;div&amp;gt;loading...&amp;lt;/div&amp;gt;
  }

  if(error) {
    return &amp;lt;div&amp;gt;{error}&amp;lt;/div&amp;gt;
  }


  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;input
        type="text"
        value={text}
        onChange={(e) =&amp;gt; setText(e.target.value)}
      /&amp;gt;
      {data.length &amp;gt; 0 ? (
        data.map((el: string, i: number) =&amp;gt; &amp;lt;div key={i}&amp;gt;{el}&amp;lt;/div&amp;gt;)
      ) : (
        &amp;lt;div&amp;gt;no results&amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Testing Async Redux Actions menggunakan Jest</title>
      <dc:creator>Ridho C Pamungkas</dc:creator>
      <pubDate>Fri, 07 Jan 2022 02:01:31 +0000</pubDate>
      <link>https://forem.com/ridhopamungkas/testing-async-redux-actions-menggunakan-jest-28b1</link>
      <guid>https://forem.com/ridhopamungkas/testing-async-redux-actions-menggunakan-jest-28b1</guid>
      <description>&lt;p&gt;Pada artikel ini saya akan sharing contoh kasus saya membuat unit testing pada ReactJS, spesifiknya pada &lt;em&gt;async redux action&lt;/em&gt;. Hal ini merupakan hal baru bagi saya dan harus membuatnya karena mendapat legacy code yang masih kosongan unit testingnya dan banyak menggunakan &lt;em&gt;async redux action&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Berikut contoh kasus untuk memanggil master data employee menggunakan redux action&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setEmployee = (data: any) =&amp;gt; ({
  dataEmployee: data,
  type: ActionTypes.SET_MASTER_EMPLOYEE,
});

export const fetchMasterEmployee = () =&amp;gt; (dispatch: any) =&amp;gt; {
  return axios.get('v1/master/employee').then((response: any) =&amp;gt; {
    if (response.status === 200) {
      const data: [] = response.data.data;
      dispatch(setEmployee(data));
    } else {
      dispatch(setEmployee([]));
    }
  })
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;jalankan perintah dibawah ini untuk menginstall package&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npm i redux-mock-store&lt;br&gt;
npm i axios-mock-adapter&lt;br&gt;
npm i redux-thunk&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;lalu import packagenya&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// untuk membuat mock store yang akan kita kirimkan action
import configureMockStore from 'redux-mock-store';
// import thunk middle untuk membuat action asynchronous
import thunk from 'redux-thunk';
// untuk mocking pemanggilan axios
import MockAdapter from 'axios-mock-adapter';
// import axios dependency
import axios from 'axios';
// inisialisasi middlewares
const middlewares = [thunk];
// inisialisasi mockstore sebagai metode configureStore yang menggunakan middlewares sebagai parameternya
const mockStore = configureMockStore(middlewares);
// membuat mock instance dari MockAdapter axios
const mock = new MockAdapter(axios);
const store = mockStore();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mari kita buat unit testnya&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; describe('Testing Action fetchMasterEmployee', () =&amp;gt; {
        beforeEach(() =&amp;gt; { 
             // Untuk membersihkan semua action yang sedang berjalan dalam store
             store.clearActions();
        });
        it('should get MASTER_EMPLOYEE', () =&amp;gt; {
             // lakukan pemanggilan urlnya, set status dan set value yang akan dibaca
             mock.onGet('v1/master/employee').reply(200, {
                data: [
                   { id: 1, name: 'Ridho' }
                ]
             });
             return store.dispatch(fetchMasterEmployee as any).then(() =&amp;gt; {
                   const expectedValueOne = [{
                       data: [{
                           id: 1,
                           name: "Ridho",
                       }],
                       type: ActionTypes.SET_MASTER_EMPLOYEE,
                   }]

             expect(store.getActions()).toEqual(expectedValueOne)
        });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;untuk method update bisa gunakan &lt;code&gt;mock.onPut&lt;/code&gt;, method insert &lt;code&gt;mock.onPost&lt;/code&gt;, dan method delete &lt;code&gt;mock.onDelete&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sekian sharing kali ini, jika ada perlu ditanyakan atau ada cara lebih baik bisa diskusi ya&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>testing</category>
    </item>
    <item>
      <title>Export data ke Excel menggunakan exceljs dari sisi Front End (ReactJs)</title>
      <dc:creator>Ridho C Pamungkas</dc:creator>
      <pubDate>Thu, 06 Jan 2022 07:12:31 +0000</pubDate>
      <link>https://forem.com/ridhopamungkas/export-data-ke-excel-menggunakan-exceljs-dari-sisi-front-end-reactjs-5743</link>
      <guid>https://forem.com/ridhopamungkas/export-data-ke-excel-menggunakan-exceljs-dari-sisi-front-end-reactjs-5743</guid>
      <description>&lt;p&gt;Pertama-tama kita kenalan dulu dengan &lt;a href="https://www.npmjs.com/package/exceljs" rel="noopener noreferrer"&gt;exceljs&lt;/a&gt;, exceljs ini sebuah package javascript yang mendukung untuk membaca, memanipulasi, menulis data dan syle spreadsheet ke XLSX dan JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alasan Memakai exceljs
&lt;/h2&gt;

&lt;p&gt;Package ini menjadi andalan saya untuk membuat excel dari sisi backend (NodeJs) maupun frontend (ReactJS). Dari segi fitur package ini cukup lengkap support untuk membuat csv dan membuat formula salah satunya, untuk styling juga sudah memadai, exceljs juga sudah support typescript, dan yang paling penting exceljs ini masih ada update untuk fitur-fitur yang baru.&lt;/p&gt;

&lt;p&gt;selanjutnya mari kita coba&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;jalankan perintah dibawah ini untuk menginstall package&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npm i exceljs &lt;br&gt;
npm i file-server&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/file-server" rel="noopener noreferrer"&gt;file-server&lt;/a&gt; ini untuk mendownload file dari browser, lalu tinggal import seperti ini&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;langsung kita coba yang simple saja&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create WorkBook
const wb = new ExcelJS.Workbook();

//properties excel    
wb.creator = 'Ridho';
wb.lastModifiedBy = 'Pamungkas';
wb.created = new Date(2022, 1, 5);
wb.modified = new Date();
wb.lastPrinted = new Date(2022, 1, 5);

// Create Sheet
const ws = wb.addWorksheet();

// Set lebar Column menggunakan key bisa dengan huruf bisa dengan index angka
ws.columns = [
    { key: 'A', width: 10, },
    { key: 'B', width: 40, },
    { key: 'C', width: 40, },
];

// Set value cell untuk title
ws.getRow(1).getCell('A').value = 'Export data ke Excel menggunakan exceljs dari sisi Front End (ReactJs)';

// Set font Style
ws.getRow(1).getCell('A').font = {
     bold: true,
     size: 16
}

// merge cell dari A1 sampai C1
ws.mergeCells('A1', 'C1')

// inisiasi pada baris ke 3 jadi Header table
const rowHeader = ws.getRow(3);

// Buat styling cell header menggunakan perulangan agar tidak per cell kita bikinnya
for (let i = 1; i &amp;lt;= 3; i++) {
      // Untuk border table
      rowHeader.getCell(i).border = {
         top: { style: 'thin' },
         left: { style: 'thin' },
         bottom: { style: 'thin' },
         right: { style: 'thin' }
      }
      // Untuk fill color cell
      rowHeader.getCell(i).fill = {
         type: 'pattern',
         pattern: 'solid',
         fgColor: { argb: '191970' },
      }
      // Untuk alignment text dalam cell
      rowHeader.getCell(i).alignment = {
         vertical: 'middle', horizontal: 'center'
      }
      // Untuk set font
      rowHeader.getCell(i).font = {
         bold: true,
         size: 11,
         color: { argb: 'FFFFFF' },
      }
}

// Isi data Header 
rowHeader.getCell(1).value = 'No';
rowHeader.getCell(2).value = 'First Name';
rowHeader.getCell(3).value = 'Last Name';

// Buat datanya menggunakan perulangan
for (let i = 1; i &amp;lt;= 5; i++) {
    const row = ws.getRow(3 + i)
    for (let index = 1; index &amp;lt;= 3; index++) {
         row.getCell(index).border = {
             top: { style: 'thin' },
             left: { style: 'thin' },
             bottom: { style: 'thin' },
             right: { style: 'thin' }
          }
     }
     row.getCell(1).value = i;
     row.getCell(2).value = `first name ${i}`;
     row.getCell(3).value = `last name ${i}`;
}

//membuat buffer file
const buf = await wb.xlsx.writeBuffer();

//download file dari browser dan menamai filenya
saveAs(new Blob([buf]), 'Sample Excel.xlsx')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasilnya akan seperti ini&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11q81ch1xmfpyr6oxnij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11q81ch1xmfpyr6oxnij.png" alt="Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Untuk lebih kompleks cara menggunakan properties exceljs bisa dibaca di &lt;a href="https://github.com/exceljs/exceljs" rel="noopener noreferrer"&gt;https://github.com/exceljs/exceljs&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
