Navigatsiya

Agar bizning RSC’larga imkon beruvchi dasturimizda quyidagi kabi bir havola bo’lsa:

<a href="/blog">Blog</a>

Ushbu havolaga bosilganda, bu butun sahifa navigatsiyasini amalga oshiradi, bu esa brauzerni serverga so’rov yuborishiga olib keladi, server esa sahifani render qilib, uni brauzerga qaytaradi. Biz ko’p yillar oldin PHP davrida shu ishlarni qilar edik va bu jarayonda bir oz noqulaylik va sekinlik hissi paydo bo’ladi. Biz yaxshirog’ini qilishimiz mumkin: RSC’lar yordamida biz oson navigatsiyani amalga oshira olamiz, bu holda state route’lar o’zgarishi o’rtasida saqlanadi.

Buni amalga oshirish uchun biz serverga navigatsiya qilmoqchi bo’lgan URL’ni yuboramiz va server bizga ushbu sahifaning JSX daraxtini qaytaradi. Keyin brauzerdagi React yangi JSX daraxti bilan butun sahifani qayta render qiladi va biz to’liq sahifani yangilamasdan yangi sahifaga ega bo’lamiz. Bu aynan biz amalga oshirishimiz kerak bo’lgan jarayon.

Klient tomonidagi o’zgartirishlar

Buni amalga oshirish uchun, biz klientdagi kodimizni biroz o’zgartirishimiz kerak. Biz dasturimizdagi barcha havolalarga standard havolani bosish harakatini to’xtatadigan va o’rniga yangi sahifa uchun serverga so’rov yuboradigan event listener’ni qo’shamiz. Buni quyidagi tarzda amalga oshirishimiz mumkin:

window.addEventListener("click", (event) => {
  if (event.target.tagName !== "A") {
    return;
  }
  
  event.preventDefault();
  navigate(event.target.href);
});

Biz event listener’ni windowga qo’shmoqdamiz, chunki bu ishlash samaradorligi uchun muhim: biz dasturimizdagi har bir havolaga event listener’ni qo’shishni xohlamaymiz, bu katta miqdordagi event listener’lari qo’shilishiga olib keladi va bu jarayonni sekinlashtirishi mumkin. O’rniga, biz windowga bitta event listener’ni qo’shamiz va bosish maqsadining havola ekanligini tekshiramiz. Bu usul event delegation deb ataladi.

Agar foydalanuvchi A elementiga bosgan bo’lsa, biz avval havolaning standard harakatini to’xtatamiz va uning o’rniga biz bir soniya ichida aniqlaydigan navigate funksiyasini chaqiramiz. Ushbu funksiya yangi sahifa uchun serverga so’rov yuboradi va keyin React uni klientda render qiladi.

Keling navigate funksiyasini ta’riflab ko’ramiz:

async function navigate(url) {
  const response = await fetch(url, { headers: { "jsx-only": true } });
  const jsxTree = await response.json();
  const element = JSON.parse(jsxTree, (key, value) => {
    if (key === "$$typeof") {
      return Symbol.for("react.element");
    }
 
    return value;
  });
  root.render(element);
}

Bu yerda biz amalga oshirayotgan jarayon juda oddiy: biz yangi sahifa uchun serverga so’rov yuboramiz, javobni React elementi sifatida deseriyalashtiramiz va keyin bu elementni dasturimizning asos(root)iga render qilamiz. Bu React’ga sahifani yangi JSX daraxti bilan qayta render qilishga olib keladi va biz to’liq sahifani yangilamasdan yangi sahifaga ega bo’lamiz. Lekin root nima? Buni tushunish uchun biz klient tomonidagi to’liq JavaScript faylini ko’rib chiqishimiz kerak:

import { hydrateRoot } from "react-dom/client";
import { deserialize } from "./serializer.js";
import App from "./App";
 
const root = hydrateRoot(document, <App />); // <- bu root
 
window.addEventListener("click", (event) => {
  if (event.target.tagName !== "A") {
    return;
  }
 
  event.preventDefault();
  navigate(event.target.href);
});
 
async function navigate(url) {
    const response = await fetch(url);
    const jsxTree = await response.json();
    const element = deserialize(jsxTree);
    root.render(element);
}

Biz sahifani dastlabki hidratsiyalashda React’dan root’ni olamiz va bu root’dan yangi elementlarni render qilish uchun foydalanamiz. Bu React ichki tomondan qanday ishlashini ifodalaydi va biz faqat React ichki ishlatadigan API’ni ishlatmoqdamiz. Bu yaxshi narsa, chunki bu biz hech qanday maxsus yoki qiyin jarayonni amalga oshirmayotganimizni anglatadi, biz faqat React’ning ochiq API’sini ishlatmoqdamiz.

Serverning javobini sozlash

Va nihoyat, bizning serverimiz jsx-only sarlavha(header)si bilan berilgan so’rovga, keyingi sahifa uchun to’liq HTML satri o’rniga faqat JSX daraxti obyektini qaytarishi kerak.

Buni quyidagicha amalga oshirishimiz mumkin:

app.get("*", async (req, res) => {
  const jsxTree = await turnServerComponentsIntoTreeOfElements(<App />);
 
  // Bu yerda maxsus kodlar bor
  if (req.headers["jsx-only"]) {
    res.end(
      JSON.stringify(jsxTree, (key, value) => {
        if (key === "$$typeof") {
          return "react.element";
        }
 
        return value;
      })
    );
  } else {
    const html = ReactDOMServer.renderToString(jsxTree);
 
    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
            <title>My React App</title>
        </head>
        <body>
            <div id="root">${html}</div>
            <script src="/static/js/main.js"></script>
        </body>
      </html>
    `);
  }
});

E’tibor bering, agar sarlavha mavjud bo’lsa, biz JSON yubormayapmiz, balki faqat string yuboryapmizmi? Chunki biz buni klient tomonda JSON.parse qilishimiz kerak va JSON.parse string’ni kutadi, JSON obyektini emas. Bu API’ga oid bir o’ziga xoslik, lekin juda ham yomon deb bo’lmaydi.

Yangi sahifalarga navigatsiya

Endi biz yangi sahifalarga to’liq sahifa yangilanishisiz navigatsiya qilish yo’lini yaratdik. Bizning RSCs imkoniyatiga ega dasturimizda barcha bog’lanish havolalari navigatsiyasi to’liq sahifa yangilanishisiz silliq va erkin o’tadi. Lekin yangilanishlar haqida nima deymiz? Yangilanishlarni qanday boshqaramiz? Keling, buni ham ko’rib chiqamiz.