DISKON TERBATAS! Masukkan kupon "skillbaru" saat checkout di kelas apa saja

Logo Koala Skodev mengetik

Skodev

Belajar coding dalam bahasa Indonesia

REACT 19 DIUMUMKAN! ๐Ÿš€, APA SAJA YANG BARU?

React 19 dirancang mengoptimalkan performa dan juga memperkenalkan fitur-fitur canggih dan terbaru yang bertujuan untuk menyederhanakan proses dan meningkatkan kinerja pengembangan web

React 19 Diumumkan! ๐Ÿš€, Apa saja yang baru?

Daftar Isi:

Fitur-fitur terbaru React 19
1. React Compiler
2. Actions
3. New hooks
4. New API: use
5. React Server Components
6. Improvements
7. Support for Document Metada...
Ringkasan

Apakah kamu seorang pengembang web yang menggunakan React? Jika iya, ada kabar gembira nih! Tim pengembang React baru saja mengumumkan rilis terbaru mereka, yaitu React 19. ๐ŸŽ‰

Sudah sekitar 2 tahun dari versi react 18.0.2 tanggal 14 Juni 2022 di umumkan. Ada beberapa ketidakpuasan dari pengguna React 18.0.2, banyak kritik yang di sampaikan oleh pengembang React di versi ini. Akhirnya tim React mengkonfirmasi akan ada rilis besar dengan versi 19.0.0. Banyak React Developer yang menantikan rilis versi 19 ini. Rilis ini dirancang untuk mengoptimalkan performa dan memperkenalkan fitur-fitur canggih yang akan menyederhanakan proses pengembangan web.

Informasi belajar ReactJS Bahasa Indonesia

Fitur-fitur terbaru React 19

Beberapa fitur baru yang ditawarkan oleh React 19 antara lain:

  1. React Compiler: React 19 memperkenalkan compiler baru yang dirancang untuk meningkatkan performa aplikasi React. Compiler ini memungkinkan pengembang untuk mengoptimalkan kode React mereka dan mengurangi waktu rendering yang diperlukan untuk aplikasi mereka. Dengan menggunakan compiler baru ini, pengembang dapat membuat aplikasi React yang lebih cepat dan responsif.

  2. Actions: React 19 memperkenalkan mode konkuren yang memungkinkan rendering yang lebih responsif dan lancar. Dengan menggunakan Concurrent Mode, aplikasi React dapat merespons input pengguna dengan lebih cepat dan memberikan pengalaman yang lebih baik.

  3. New hook: React 19 juga memperkenalkan hook baru yaitu:

    • useActionState: Hook ini memungkinkan pengembang untuk mengelola state aplikasi dengan lebih mudah dan efisien.
    • useFormStatus: Hook ini memungkinkan pengembang untuk melacak status form dengan lebih baik dan memberikan feedback yang lebih baik kepada pengguna.
    • useOptimistic: Hook ini memungkinkan pengembang untuk melakukan operasi optimistik dengan mudah dan aman.
  4. New API: use - React 19 juga memperkenalkan API baru yaitu use. API ini memungkinkan pengembang untuk menggunakan fitur-fitur baru React dengan lebih mudah dan efisien. Dengan menggunakan API use, pengembang dapat mengakses fitur-fitur baru React tanpa perlu mengubah kode yang sudah ada.

  5. React Server Components: Salah satu fitur paling menarik dari React 19 adalah Server Components. Fitur ini memungkinkan pengembang untuk membuat komponen yang di-render di sisi server dan dikirimkan ke klien sebagai HTML statis. Hal ini dapat meningkatkan performa dan mengurangi waktu muat aplikasi.

  6. Improvements: React 19 juga melalukan improvement yaitu:

    • ref as a prop: di React 19 kita dapat menggunakan ref sebagai props, ini sangat memudahkan untuk memanipulasi ref yang akan di gunakan di child Components.
    • Diffs for hydration errors: React 19 juga membuat lebih simple handle error log untuk hydration error, yang sebelumnya multiple error log ketika membuka browswe console, sekarang di React 19 hanya di buat 1 baris jadi lebih mudah untuk di baca.
    • <Context> as a provide: ini juga menarik memudahkan developer untuk membaca code dikarenakan code jadi lebih bersih. di React 19 bisa menggunakan <Context> sebagai provider.
    • Cleanup functions for refs: React 19 juga memperbaiki fitur ref yang mana dapat melakukan cleanup ketika element sudah di hapus dari DOM.
    • useDeferredValue initial value: adalah React Hook yang memungkinkan developer untuk menunda pembaharuan sebagian UI.
  7. Support for Document Metadata: React 19 juga mendukung metadata dokumen yang lebih baik. Dengan menggunakan fitur ini, pengembang dapat mengatur metadata dokumen dengan lebih mudah dan efisien.

Seperti yang saya sampaikan sebelumnya bahwa React 19 memiliki banyak kritik dari para developer yang mana masalah rendering ulang yang berlebihan,banyak developer menghabiskan banyak waktu berjam jam untuk mengatasi masalah ini yang sering kali menyebabkan kinerja developer menurun. Developer mencoba memperbaiki masalah rendering ulang dengan menggunakan teknik-teknik yang ada, seperti menggunakan React.memo, useMemo, useCallback, dan lain-lain. Namun, teknik-teknik tersebut seringkali tidak efektif dan memakan waktu yang lama. Dengan adanya React 19, pengembang React dapat mengoptimalkan performa aplikasi mereka dan menggunakan fitur-fitur baru yang akan menyederhanakan proses pengembangan web.

Saya akan mencoba untuk menjelaskan secara rinci fitur-fitur baru yang ditawarkan oleh React 19.

1. React Compiler

React Compiler adalah compiler baru yang dirancang untuk meningkatkan performa aplikasi React. Compiler ini memungkinkan pengembang untuk mengoptimalkan kode React mereka dan mengurangi waktu rendering yang diperlukan untuk aplikasi mereka. Dengan menggunakan compiler baru ini, pengembang dapat membuat aplikasi React yang lebih cepat dan responsif.

Kenapa React Compiler ini penting? Karena dengan adanya compiler ini, pengembang dapat mengoptimalkan kode React mereka dan mengurangi waktu rendering yang diperlukan untuk aplikasi mereka. Dengan menggunakan compiler baru ini, pengembang dapat membuat aplikasi React yang lebih cepat dan responsif. Sebagai contoh, kamu memiliki React Component yang menghasilkan Object atau Array baru pada setiap render.

import React from "react";

const AlphabetList = () => {
  // define the alphabet array
  const alphabet = Array.from({ length: 26 }, (_, i) =>
    String.fromCharCode(65 + i)
  );

  return (
    <div>
      <h2>Alphabet List</h2>
      <ul>
        {/* merender alphabet sebagai list items */}
        {alphabet.map((letter, index) => (
          <li key={index}>{letter}</li>
        ))}
      </ul>
    </div>
  );
};

export default AlphabetList;

Meskipun konten dari komponen ini tidak berubah / sama, React tidak akan mengetahui hal tersebut secara efisien, akibatnya React akan merender ulang komponen ini setiap kali komponen induknya merender ulang. Mekanisme rendering ulang ini dapat dengan cepat menjadi tidak terkendali, sehingga berdampak signifikan terhadap kinerja aplikasi dan kurang baik di sisi user.

untuk mengatasi masalah ini, kita bisa menggunakan React.memo atau useMemo untuk menghindari rendering ulang yang tidak perlu. dan mungkin useCallback juga bisa di gunakan untuk menghindari rendering ulang yang tidak perlu. code di atas akan kita optimalkan dengan menggunakan useMemo seperti ini:

import React, { useMemo } from "react";

const AlphabetList = () => {
  // define the alphabet array via useMemo()
  const alphabet = useMemo(() => {
    return Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i));
    // no dependencies, so it will only be calculated once on the first render
  }, []);

  return (
    <div>
      <h2>Alphabet List</h2>
      <ul>
        {/* render the alphabet as list items */}
        {alphabet.map((letter, index) => (
          <li key={index}>{letter}</li>
        ))}
      </ul>
    </div>
  );
};

export default AlphabetList;

Gimana rumit tidak? kita harus menggunakan useMemo untuk menghindari rendering ulang yang tidak perlu. Dengan adanya React Compiler, kita tidak perlu lagi menggunakan useMemo untuk menghindari rendering ulang yang tidak perlu. Compiler ini akan mengoptimalkan kode React kita dan mengurangi waktu rendering yang diperlukan untuk aplikasi kita. Tim React telah membuat โ€œReact Compilerโ€. Kompiler React sekarang akan mengelola perenderan ulang ini. React akan memutuskan secara otomatis bagaimana dan kapan mengubah status dan memperbarui UI.

Jadi, dengan adanya React Compiler baru di React 19 ini, pengembang tidak perlu lagi khawatir tentang rendering ulang yang tidak perlu. Compiler ini akan mengoptimalkan kode React kita dan mengurangi waktu rendering yang diperlukan untuk aplikasi kita.

2. Actions

React 19 mengenalkan Action yaitu sebuah fitur yang menyederhanakan dalam pengelolaan data mutations (fetching data ke API) atau state di aplikasi React. Dengan menggunakan Actions, developer dapat mengelola pending state, error, optimistic update dan request secara berurutan tidak dengan secara manual lagi. Actions ini memungkinkan developer untuk mengelola state aplikasi dengan lebih mudah dan efisien.

Kasus paling banyak di aplikasi React adalah ketika melakukan data mutation (fetching data ke API) dan kemudian response data dari API akan di update ke state. Sebagai contoh lebih spesifik lagi yaitu ketika user ingin mengupdate data profile, maka akan melakukan request ke API, kemudian API akan meresponse data yang di update ke state. Saya akan memberikan contoh untuk menghandle kasus tersebut cara manual dan menggunakan Action.

Cara Manual

// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    // Fetch data from API
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    }
    redirect("/path");
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

Jika kita bedah code di atas, kita bisa lihat bahwa kita harus mengelola state isPending secara manual. Hal ini membuat code kita menjadi lebih kompleks dan sulit untuk di maintain.

Menggunakan Actions

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      }
      redirect("/path");
    });
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

Jika kita bedah lagi code di atas, kita bisa lihat bahwa kita tidak perlu lagi mengelola state isPending secara manual. useTransition akan mengelola state isPending dengan nilai awal adalah true dan akan menjadi false ketika proses selesai. Hal ini memungkinkan developer menjaga UI agar tetap responsif dan interaktif saat data berubah. Gimana menurut kalian? lebih mudah dan efisien bukan?

3. New hooks

React 19 juga memperkenalkan beberapa hooks baru yang dapat membantu pengembang dalam mengelola state lebih mudah. Berikut adalah beberapa hooks baru yang diperkenalkan di React 19 dan akan kita bedah 1 per 1:

3.1. useActionState

useActionState adalah hooks yang digunakan untuk mengelola state dengan cara yang lebih deklaratif. Hooks ini memungkinkan kita untuk mendefinisikan aksi-aksi yang dapat mempengaruhi state dan mengubah state berdasarkan aksi-aksi tersebut. Berikut adalah contoh penggunaan useActionState di kasus yang sebelumnya yaitu update profile:

// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
      return null;
    },
    null
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </form>
  );
}

Dengan kasus yang sama kita dapat mensederhanakan kode dengan menggunakan useActionState hooks. yang mana di dalam fungsi useActionState terdapat fungsi submitAction yang dapat langsung di panggil dari form action.

3.2. useFormStatus

useFormStatus adalah hooks yang memudahkan dalam membuat suatu design system, misalnya kita ingin mengakses informasi yang ada di dalam form tanpa harus menggunakan props di dalam komponen, kita dapat menggunakan hooks ini. walaupun kasus ini dapat di selesaikan juga menggunakan Context namun kita harus membuat Provider dan Consumer yang mana akan membuat kode menjadi lebih kompleks.

Berikut adalah contoh penggunaan useFormStatus hooks:

import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";

function Submit() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}

function Form({ action }) {
  return (
    <form action={action}>
      <Submit />
    </form>
  );
}

export default function App() {
  return <Form action={submitForm} />;
}

3.3. useOptimistic

useOptimistic hook ini akan membantu meningkatkan pengalaman pengguna dan akan menghasilkan respons yang lebih cepat. Ini akan berguna untuk aplikasi yang perlu berinteraksi dengan server. hook ini akan segera memperbarui UI dengan asumsi permintaan akan berhasil. Dinamakan optimistic karena pengguna akan melihat hasil optimistic (success) dari melakukan suatu tindakan, padahal sebenarnya tindakan tersebut membutuhkan waktu untuk diselesaikan.

import { useOptimistic, useState } from "react";

const Optimistic = () => {
  const [messages, setMessages] = useState([
    { text: "Hey, I am initial!", sending: false, key: 1 },
  ]);
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true,
      },
    ]
  );

  async function sendFormData(formData) {
    const sentMessage = await fakeDelayAction(formData.get("message"));
    setMessages((messages) => [...messages, { text: sentMessage }]);
  }

  async function fakeDelayAction(message) {
    await new Promise((res) => setTimeout(res, 1000));
    return message;
  }

  const submitData = async (userData) => {
    addOptimisticMessage(userData.get("username"));

    await sendFormData(userData);
  };

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={submitData}>
        <h1>OptimisticState Hook</h1>
        <div>
          <label>Username</label>
          <input type="text" name="username" />
        </div>
        <button type="submit">Submit</button>
      </form>
    </>
  );
};

export default Optimistic;
  1. fakeDelayAction adalah fungsi yang akan menunda eksekusi selama 1 detik.
  2. submitData adalah fungsi yang akan menambahkan pesan optimistic ke daftar pesan dan kemudian mengirimkan data ke server.
  3. sendFormData adalah fungsi untuk mengirim data form ke fakeDelayAction.

Didalam submitData kita menggunakan addOptimisticMessage ini akan menambahkan form data jadi itu akan ada di dalam optimisticMessage. Kita akan menggunakan ini untuk menampilkan pesan di dalam UI.

{
  optimisticMessages.map((message, index) => (
    <div key={index}>
      {message.text}
      {!!message.sending && <small> (Sending...)</small>}
    </div>
  ));
}

4. New API: use

React 19 juga memperkenalkan beberapa hooks baru yaitu use. Hook ini akan menyederhanakan bagaimana kita mengelola Promise yang mana React akan menunggu sampai Promise tersebut terselesaikan. Promise adalah objek yang merepresentasikan hasil dari suatu eksekusi asynchronous. Promise dapat berada dalam 3 keadaan yaitu pending, fulfilled, dan rejected. Kembali ke hooks use, berikut ini adalah contoh penggunaan hooks use:

import { use } from "react";

const fetchUsers = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  return res.json();
};

const UsersItems = () => {
  const users = use(fetchUsers());

  return (
    <ul>
      {users.map((user) => (
        <div key={user.id} className="bg-blue-50 shadow-md p-4 my-6 rounded-lg">
          <h2 className="text-xl font-bold">{user.name}</h2>
          <p>{user.email}</p>
        </div>
      ))}
    </ul>
  );
};
export default UsersItems;

Dari kode di atas kita menggunakan use untuk mengeksekusi fungsi fetchUsers yang mengambil data dari API. Kita menggunakan use untuk menunggu sampai Promise tersebut terselesaikan.

Sebagai catatan bahwa hooks use tidak dapat di gunakan fungsi promise yang di buat di dalam render. Jika kita mencoba menjalankannya di dalam render maka akan di dapati warning seperti ini A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.

Cara lain untuk menggunakan hooks use adalah di gabungkan dengan Context.

import { use } from "react";
import ThemeContext from "./ThemeContext";

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

function Heading({ children }) {
  if (children == null) {
    return null;
  }

  // ini tidak akan bekerja menggunakan useContext
  // karena di awal return.
  const theme = use(ThemeContext);
  return (
    <h1 style={{ color: theme === "light" ? "red" : "white" }}>{children}</h1>
  );
}

5. React Server Components

Ini hal yang menarik di React 19 yang mungkin kita tahu react sebelumnya hanya dapat berjalan di Client Side Rendering yaitu rendering di sisi client. sebelumnya kita tahu react yang dapat di render di sisi server (Server Side Rendering) yaitu sebuah framework nextjs. Namun di React 19, React memperkenalkan fitur baru yaitu React Server Components. React Server Components adalah komponen yang di render di server dan di kirim ke client sebagai HTML biasa. React Server Components memungkinkan kita untuk mengirimkan komponen yang di render di server ke client sebagai HTML biasa. Ini akan mengurangi waktu loading halaman dan juga mengurangi beban client. Sama halnya dengan NextJS semua komponent pada dasarnya adalah server side rendering, namun ketika di berikan code use client maka NextJS akan merender di client side. Sepertinya React 19 mengadopsi konsep ini.

"use client";

type ClientComponentProps = {
  children: React.ReactNode,
};

export default function ClientComponent({ children }: ClientComponentProps) {
  console.log("client rendered");
  return <>{children}</>;
}
export default function ServerComponent() {
  console.log("server rendered");
  return <></>;
}

Jadi bagaimana membedakan antara komponen yang di render di server dan di client? kita dapat mengggunakan console di browser, jika kita menjalankan app di mode client maka kita dapat melihat console client rendered di console browser, dan jika kita menjalankan app di mode server maka kita dapat melihat console server rendered di console terminal.

Namun react memberikan catatan bahwa React Server Components masih dalam tahap eksperimen dan belum siap untuk digunakan di produksi. Jika tidak ada break di antara major version, namun jika tidak ada break dan sudah stable maka kita dapat menggunakan React Server Components di produksi.

6. Improvements

Saya akan mencoba membreakdown beberapa perubahan yang ada di React 19:

6.1. ref as a prop

Saat ini react dapat menggunakan ref sebagai props, yang mana di versi sebelum nya kita harus mengambil ref props menggunakan forwardRef yang mana cukup merepotkan dan susah untuk membaca code nya. Berikut ini adalah contoh penggunaan ref sebagai props:

function MyInput({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

//...
<MyInput ref={ref} />;

6.2. Diffs for hydration errors

Seperti yang sudah saya jelaskan di part 1 yaitu React 19 juga membuat lebih simple handle error log untuk hydration error, yang sebelumnya multiple error log ketika membuka browswe console, sekarang di React 19 hanya di buat 1 baris jadi lebih mudah untuk di baca.

6.3. <Context> as a provide

<Context> sebagai provider yang di versi sebelumnya jika kita ingin menggunakan Context kita harus membuat Provider terlebih dahulu, di versi 19 kita dapat menggunakan Context tanpa harus mendefenisikan provider, berikut akan saya berikan contoh antara menggunakan Provider dan tidak menggunakan di versi React 19:

// Cara lama di versi < 19
function App() {
  const [theme, setTheme] = useState("light");
  // ...
  return (
    <ThemeContext.Provider value={theme}>
      <Page />
    </ThemeContext.Provider>
  );
}
// Cara baru di versi 19
const ThemeContext = createContext("");

function App({ children }) {
  return <ThemeContext value="dark">{children}</ThemeContext>;
}

gimana lebih simple bukan?.

6.4. Cleanup functions for refs

React 19 juga membuat ref sebagai function dan dapat di clear, berikut contohnya:

<input
  ref={(ref) => {
    // ref dibuat

    // BARU: return untuk membersihkan / reset ref
    // ketika element sudah di hapus dari DOM.
    return () => {
      // fungsi
    };
  }}
/>

6.5. useDeferredValue initial value

useDeferredValue adalah React hook yang mengijinkan untuk memisahkan UI kedalam prioritas yang tinggi atau prioritas yang rendah. Ia bekerja dengan membiarkan React menginterupsi dirinya sendiri ketika sesuatu yang penting terjadi. Contoh yang lebih sederhana perhatikan kode ini:

function App() {
  const [count, setCount] = React.useState(0);
  const deferredCount = React.useDeferredValue(count);
  return (
    <>
      <ImportantStuff count={count} />
      <SlowStuff count={deferredCount} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}

Untuk render awal count dan deferredCount nilainya sama persis (0). Namun, ketika pengguna mengklik tombol Increment, sesuatu yang menarik terjadi? Setiap render kini menampilkan nilai hitungan serta nilai tangguhan yang kita teruskan ke <SlowStuff>.

7. Support for Document Metadata

Pada React.js 19, kita dapat menggunakan tag title, link dan meta secara langsung pada komponen React kita. Fitur ini dapat membantu meningkatkan SEO (Search Engine Optimization) dan memastika aksebilitas.Berikut contoh penggunaan tag title dan meta:

function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Josh" />
      <link rel="author" href="https://twitter.com/joshcstory/" />
      <meta name="keywords" content={post.keywords} />
      <p>Eee equals em-see-squared...</p>
    </article>
  );
}

Ringkasan

  1. Akan ada kompiler React baru yang akan diperkenalkan di versi React yang akan datang.
  2. React 19 akan melakukan rendering ulang otomatis, memoisasi, dan optimalisasi status dan UI.
  3. Di perbaharui dan ditambahkan Hooks baru yang akan mempermudah pengembangan aplikasi.
  4. React 19 dapat melakukan compiling di server side dan client side.
  5. React 19 memiliki penanganan form yang lebih baik menggunakan Actions, useActionState, useFormStatus dan useOptimistic.

Dari semua pembaharuan yang ada di React 19, saya sendiri sangat menantikan fitur React Compiler dan new Hooks dimana React Compiler sangat membantu dalam mengoptimalkan performa aplikasi React yang selama ini sering saya dapati di beberapa project saya dan sangat susah untuk mengoptimalkan performa aplikasi di React. dan juga New Hook yang mempermudah dalam mengelola state aplikasi dengan lebih mudah dan efisien. Jika ada yang ingin ditanyakan atau di diskusikan silahkan tulis di kolom komentar.

Sumber:

Penulis: Amril Syaifa Yasin

/ @amrilsyaifay

Hai saya Amril, saya seorang software engineer frontend yang saat ini bekerja di Quipper, namun di sisi lain saya juga punya beberapa pengalaman sebagai fullstack engineer. semoga saya dapat membagikan ilmu yang bermanfaat untuk sahabat-sahabat semua.

Daftar newsletter skodev masukkan emailmu untuk dapat informasi menarik dari dunia koding