Cara Menangani Responsive di NextJS ( SSR )

Permasalahan

Saat menggunakan library react-responsive dan menggabungkannya dengan NextJS serta Twin's /  twin.macro saya mendapatkan permasalahan. Saat menggunakan react-responsive, salah dalam melakukan render styled-component. Misalnya komponen A dengan style A pada dekstop version, tetapi yang dirender adalah komponen A dengan style B pada dekstop version.

Saya pun mencoba mencari penyebab permasalahan tersebut. Akhirnya menemukan artikel yang ditulis Nitay Neeman dalam Blognya berjudul "React - Combining Server-Side Rendering and Responsive Design"

Apa yang dituliskan Nitay tersebut, memberikan beberapa point penting bagi saya yang sebelumnya tidak pernah saya pikirkan.

  1. Menggunakan NextJS, yang merupakan SSR. Sedangkan Server-side tidak tidak bisa mengenali window yang digunakan untuk mengenali viewport pada sebuah dokumen
  2. Karena tidak bisa mengenai, tentu saja berpotensi untuk terjadinya galat, yang dalam tulisan tersebut sempat memention hydration() yang sampai saat ini saya masih belum memahmi 100% apa maksud dari hal tersebut.

Nitay, dalam blognya juga memberikan sejumlah rekomendasi, salah satunya adalah menggunakan Library https://github.com/artsy/fresnel yang mirip dengan react-responsive . Sayangnya, secara sederhananya, pustaka ini menambahkan sebuah element div baru untuk bisa membuat element child menjadi kondisional render berdasarkan viewport. Walaupun, saya belum lagi melakukan exploring saat digabungkan dengan react context .

import React from "react"
import { Media } from "./Media"

export const HomePage = () => {
  return (
    <>
      <Media at="sm">Hello mobile!</Media>
      <Media greaterThan="sm">Hello desktop!</Media>
    </>
  )
}
Penambahan Komponen Media

Solusi

Penggunaan Display Hidden

Tentu saja kembali pada cara yang sederhana dan menyelesaikan banyak masalahm yaitu menggunakan display:none untuk bisa menghilangkan secara visual sebuah element HTML. Ya, tentu saja secara visual element HTML sudah menghilang. Namun, pada DOM Tree, element ini masih ada. Cara ini juga direkomendasikan oleh mas Resi.

Penggunaan Element Picture untuk Gambar

Semantic HTML ini masih kurang populer digunakan oleh banyak Frontend Developer, mengingat untuk menggunakannya, kita harus menyiapkan beberapa gambar sesuai viewport yaitu <picture> </picture> yang bisa dibaca pada picture w3school.

Cara ini bisa digunakan apabila saat menggunakan display:none pada element yang memiliki sebuah gambar. Karena, walaupun kita menggunakan display:none namun, element tetap akan mengunduh gambar. Karena memang display:none berfungsi hanya untuk menghilangkan element secara visual saja.

Menggunakan NextJS Image

Tentu saja, saat berurusan dengan gambar, sejak NextJS versi 10, saya sebisa mungkin menggunakan Next Image yang merupakan salah satu keunggulannya dalam mengoptimalkan gambar. Next Image membuat gambar dioptimasi dengan mengubah kualitas, dan size menyesuaikan dengan viewport sehingga cukup dengan satu sumber gambar bisa memberikan hasil gambar berbeda sesuai dengan viewport

Menggunakan Custom Hooks di react-responsive

Solusi inilah yang akhirnya saya gunakan setelah seharian mencari di github react-responsive , dan juga Mbah Google, saya menemukan cara yang efektif untu tetap menggunakan react-responsive namun bisa bekerja secara optimal di NextJS atau SSR.

Saya menemukan diskusinya pada tautan github dibawah ini :

[SSR] Rehydrating app contains the wrong classnames. · Issue #162 · contra/react-responsive
I use the match render prop callback to set a prop which itself toggles a className. This renders fine on the server but on first load the client does not re-render to correctly display the classNa...

Saya pun menggunakan Hooks sesuai dengan rekomendasi salah satu komentar pada diskusi tersebut.

Custom Hooks React Responsive

Kurang lebih, custom Hooksnya nanti seperti ini

import { useState, useEffect } from 'react';
import { useMediaQuery } from 'react-responsive';

function useResponsive() {
  const [isClient, setIsClient] = useState(false);

  const isOnlyMobile = useMediaQuery({
    maxWidth: '640px',
  });
  const isMobile = useMediaQuery({
    minWidth: '640px',
  });

  const isTablet = useMediaQuery({
    minWidth: '768px',
  });

  const isDesktop = useMediaQuery({
    minWidth: '1024px',
  });

  useEffect(() => {
    if (typeof window !== 'undefined') setIsClient(true);
  }, []);

  return {
    isDesktop: isClient ? isDesktop : true,
    isTablet: isClient ? isTablet : false,
    isMobile: isClient ? isMobile : false,
    isOnlyMobile: isClient ? isOnlyMobile : false,
  };
}

export default useResponsive;
Custom Hooks untuk React Responsive

Sehingga, nanti kita tinggal menggunakannya seperti ini

...
import useResponsive from '@/utils/useResponsive';
...

const Component = () => {
const { isMobile, isOnlyMobile } = useResponsive();
 
 return (
	 <>
		 {isMobile ? <ComponentTablet/> : <ComponentMobile/>
	 </>
 )
}

Pertanyaan Baru.

Kenapa menggunakan react-responsive atau menggunakan pustaka baru?

  1. Untuk Condition Rendering misalkan kita perlu meload komponen A untuk  Mobile namun meload komponen B untuk Dekstop
  2. Supaya benar-benar hilang dari DOM Tree, karena bisa jadi pada komponen tersebut saya melakukan pemanggilan API dan saat mode Mobile pemanggilan API dilakukan oleh komponen lain.

Kurang lebih, seperti itu cerita saya dalam menangani responsive design di NextJS dengan SSR-nya dan tetap menggunakan react-responsive untuk melakukan Condition Rendering pada komponen ReactJS.