DEV Community

Cover image for Meningkatkan User Experiece (UX) Aplikasi Menggunakan useOptimistic Hooks pada React JS/Next JS
Yoga Meleniawan Pamungkas
Yoga Meleniawan Pamungkas

Posted on • Edited on

Meningkatkan User Experiece (UX) Aplikasi Menggunakan useOptimistic Hooks pada React JS/Next JS

Image description

Seberapa penting emang user experience (UX) dari user terhadap aplikasi yang kita kembangkan? Sangat penting bro. Kalau first impression dari user terhadap aplikasi kita sudah jelek gara-gara user experience yang tidak ramah lingkungan, maka user pun juga males buat buka aplikasi yang kita kembangkan. Emang ada parameternya buat nentuin bahwa UX aplikasi kita udah ramah lingkungan? Ada, parameter yang paling minimal itu penggunaan _loading _ ketika melakukan pemrosesan data/perubahan state.

Pada React JS kita bisa meningkatkan UX dari aplikasi yang kita kembangkan menggunakan useOptimistic Hooks yang bisa temen-temen baca dokumentasinya disini sebelum masuk lebih dalam dan implementasinya. Perlu diketahui ya temen-temen useOptimistic ini hanya tersedia pada React Canary dan Experimental Channel. Tapi, bukan berarti temen-temen kalau tidak menggunakan React Canary tidak mau berkenalan dengan useOptimistic Hooks. Tidak ada salahnya mencari ilmu baru.

Kira-kira dari 2 perbandingan di bawah ini, temen-temen lebih prefer memilih yang mana?

GIF 1. Tanpa menggunakan useOptimistic Hooks:
Image description

GIF 2. Menggunakan useOptimistic Hooks:
Image description

Terdapat perbedaan yang cukup signifikan dari implementasi useOptimistic Hooks pada komponen React JS/Next JS. Gif 1 tidak memberi tahu bahwa ada proses yang sedang terjadi ke backend ketika menambahkan data baru, sedangkan Gif 2 memberi tahu kepada user dengan menambahkan opacity ketika ada proses pengiriman data ke backend. Saya pribadi lebih memilih opsi yang kedua karena ketika kita membuat sebuah aplikasi pastinya kita ingin user dapat berinteraksi dengan aplikasi kita sehingga user experience dapat terbentuk hanya dengan hal yang cukup sederhana seperti ini.

Oke, kita lanjut ke tahap implementasi kode. Kali ini saya menggunakan Next JS untuk implementasi useOptimistic Hooks ini. Disini temen-temen juga bisa praktik dengan menjalankan kode program yang sudah saya buat ya.

Image description

"use client"

import { useState } from "react";
import { Todo } from "./Todo";

async function deliverTodos(todo: string) {
  await new Promise((res) => setTimeout(res, 1000));
  return todo;
}

export default function Home() {
  const [todos, setTodos] = useState([
    { text: "Mengerjakan Tugas", sending: false }
  ]);
  async function sendTodos(formData: any) {
    const sentTodo = await deliverTodos(formData.get("todo"));
    setTodos((todos: any) => [...todos, { text: sentTodo }]);
  }
  return (
    <main className="h-screen w-screen flex items-center justify-center">
      <div className="flex flex-col">
        <Todo todos={todos} sendTodos={sendTodos} />
      </div>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Lanjut, kita akan coba breakdown terlebih dahulu kode program yang sudah saya buat.

Image description

Function deliverTodos ini kita analogikan sebagai function yang digunakan untuk melakukan request ke backend dengan durasi request dan response 1 detik. Jadi, kalau temen-temen udah punya endpoint sendiri bisa diimplementasikan pada function ini.

Image description

Kita membutuhkan 1 state yaitu todos sebagai initiator value dan digunakan untuk menyimpan perubahan value yang akan kita gunakan pada useOptimistic Hooks.

Image description

/* eslint-disable react/jsx-key */
"use client";
import { useOptimistic, useRef } from "react";

export function Todo({ todos, sendTodos }: any) {
    const formRef = useRef<HTMLFormElement>(null);

    async function formAction(formData: FormData) {
        addOptimisticTodo(formData.get("todo"));
        formRef.current?.reset();
        await sendTodos(formData); // proses penambahan data ke database secara async
    }

    const [optimisticTodo, addOptimisticTodo] = useOptimistic(
        todos,
        (state, newTodo) => [
            ...state,
            {
                text: newTodo,
                sending: true
            }
        ]
    );

    return (
        <>
            <h1 className="text-black">Todo List Application</h1>
            <div className="flex flex-col space-y-2 my-2">
                {optimisticTodo.map((todo: any, index: number) => (
                    <div className="w-full">
                        <div
                            className={`bg-gray-100 
                        rounded flex p-4 h-full 
                        items-center text-black 
                        ${!!todo.sending ? 'opacity-20' : ''}`}>
                            <svg
                                fill="none"
                                stroke="currentColor"
                                stroke-linecap="round"
                                stroke-linejoin="round"
                                stroke-width="3"
                                className="text-indigo-500 w-6 h-6 flex-shrink-0 mr-4"
                                viewBox="0 0 24 24">
                                <path d="M22 11.08V12a10 10 0 11-5.93-9.14"></path>
                                <path d="M22 4L12 14.01l-3-3"></path>
                            </svg>
                            <span className="font-medium">{todo.text}</span>
                        </div>
                    </div>
                ))}
            </div>
            <form action={formAction} ref={formRef} className="flex flex-col">
                <input type="text"
                    className="bg-gray-50 border 
                border-gray-300 text-gray-900 text-sm rounded-lg 
                focus:ring-blue-500 focus:border-blue-500 block 
                w-full p-2.5 dark:bg-gray-200 dark:border-gray-200 
                dark:placeholder-gray-400 dark:text-black 
                dark:focus:ring-red-500 dark:focus:border-blue-500 
                my-5"
                    name="todo"
                    placeholder="Add Todo Today"
                    autoComplete="off" />
                <button
                    type="submit"
                    className="
                text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 
                focus:outline-none focus:ring-blue-300 
                font-medium rounded-lg text-sm w-full 
                sm:w-auto px-5 py-2.5 text-center 
                dark:bg-blue-600 dark:hover:bg-blue-700 
                dark:focus:ring-blue-800">Add</button>
            </form>
        </>
    );
}
Enter fullscreen mode Exit fullscreen mode

Kita akan breakdown lagi kode program yang ada pada component.

Image description

function formAction merupakan function yang digunakan untuk menambahkan hasil input todo ke dalam addOptimisticTodo yang mana function ini merupakan function yang akan kita definisikan pada useOptimisitic destruct. Lalu, kita bisa mengirim data ke API secara asynchronous.

Image description

Lalu kita bisa mendefinisikan passthrough dan reducer yang ada pada useOptimistic Hooks. Disini saya definisikan passthrough dengan nama optimisticTodo dan reducer sebagai addOptimisticTodo. Kemudian, didalam parameter useOptimistic kita bisa kirimkan todos sebagai passthrough dan menuliskan reducer sesuai dengan dokumentasi yang sudah disediakan oleh React JS. Di dalam reducer kita kirim 2 value yaitu text dan sending. Dengan demikian, implementasi useOptimistic sudah bisa digunakan.

Image description

Sekarang, kita bisa langsung gunakan state useOptimistic pada html tag komponennya seperti kode program diatas. Bagaimana? cukup mudah bukan implementasi useOptimistic Hooks ini pada React JS/Next JS?

Dengan menggunakan useOptimistic Hooks, sekarang aplikasi yang temen-temen kembangkan sudah memenuhi syarat minimal untuk meningkatkan user experience. Cukup sekian ya artikel sederhana yang bisa saya tuliskan, semoga kita bisa bertemu di artikel lain dengan materi yang lebih menarik lagi.

Top comments (0)