WordPress + React e (și nu e) o idee bună

M-am apucat să-mi redesenez tema blogului prin iulie, cred. Mi-a luat câteva ore să fac prima pagină, după care m-am luat cu altele și a zăcut pe Desktop vreo câteva luni. După care mi-a încolțit în minte un gând: frontend-ul scris în React, backend-ul folosit doar pentru admin (mă rog, și ca sursă de conținut prin API-uri).

Acu’ vreo două luni, am început să mă joc mai serios. Am pornit un server de Node pe laptop, mi-am bătut capul cu Cross-Origin până mi-a luat foc, am rezolvat bucata aia și am început să trag date. Asta se face cam așa:

https://gist.github.com/mhlnu/7221782c7518cfaae29dc7e567d7aef2

Eh, și-aici începi să te scarpini după cap un pic. Iată câteva idei:

  • API-urile din WordPress sunt destul de rapide, doar că, uneori, mai gâfâie. M-am trezit, de vreo două ori, că am primit un response după patru secunde. React e rapid, per total, și randarea se face instant din momentul în care a primit JSON-ul, dar…
  • nu dau vina pe vreo bibliotecă de fetching, lucrez cu axios de un an deja, e foarte rapid și foarte ușor de folosit, dau vina pe server
  • formatul JSON folosit de WordPress e stupid și incomplet în forma inițială. Pe scurt, în formatul default pentru /posts, singura informație despre autor e ID-ul utilizatorului. Nu tu nume, nu tu link, nimic. Tag-urile și categoriile așijderea, doar ID-ul. Atașamentele (pozele) lipsă. Meta fields, la fel, pauză.
  • după ce-am pierdut o groază căutând informații (documentația oferită de WordPress pentru REST APIs e scrisă cu picioarele), am aflat că poți trage datele alea folosind filtre. API-ul complet e, de fapt, /posts?_embed
  • eh, și de-aici începe distracția, că tot adaugi chestii pe măsură ce înaintezi. /posts?_embed&per_page=20&page=2 și tot așa. Dar până te prinzi, ohoho, durează
  • API-ul pentru conținutul unei postări ai zice că tre’ să fie ceva logic, gen /post?id=12345 și primești tot, inclusiv comentarii, thumbnail etc. Nu e. E /posts?_embed&slug=cuvintele-din-permalink. Mă rog, poți folosi și ID-ul. Eu am ales să folosesc slug din cauză de React Router, deja avea o informație, nu mai avea nici un sens să mai pasez încă una: <Route exact path='/:slug' component={ Article } />
  • postările nu vin la pachet cu comentarii, iar asta e din cauză că API-ul folosit e cel general, nu există unul dedicat. Sau, mă rog, vin cu niște comentarii (fix zece, nu știu dacă primele sau ultimele) și, evident, de ce-ar fi folosit comments în JSON când puteau să folosească… replies?
  • prin urmare, când randezi pagina, tre’ să mai folosești un API, pe cel de comentarii: /comments?post=123&per_page=100&order=desc e cam standard. De ce așa complicat? Păi… pentru că nu poți să le încarci pe toate și vin într-o ordine tâmpită. Și vin maximum 100, nu există opțiune de tip per_page=all
  • evident, comentariile nu sunt grupate în funcție de părinți/copii, ci la grămadă. Soluția pentru nested comments? Ceva de genul ăsta:

https://gist.github.com/mhlnu/37585c2633e6f0a0a148f72d231ca06a

  • nu neg că se putea face într-un mod mai eficient (sigur o să fiu tras de urechi pentru abordarea asta), da’ mi-a fost efectiv lene să-mi bat capul mai mult de-atât, ba sortare după ID, ba sortare după copii, ba sortare după dată etc.
  • alte trăznăi sunt lipsa unor endpoint-uri pentru meniuri sau chestii de genul post._embedded['wp:featuredmedia'][0].media_details.sizes['medium'].source_url pentru a ajunge la thumbnail. Știu, GraphQL, bla bla.

Mă rog, sunt multe d-astea și aș putea scrie o carte doar despre formatul JSON-urilor.

Eh, la toate astea, se mai adaugă alte complicații, că durează un pic până te prinzi cum folosești Router ba pentru arhivele pentru tag-uri sau pentru categorii, ba pentru pagini. Mă rog, nu e greu, Google e prietenos, dar cea mai mare frustrare e legată de modul în care îți sunt livrate datele.

Altă chestie care-mi stă pe creier e că n-am găsit încă o formulă pentru caching local. Mi se pare un pic stupid să mă apuc să folosesc localStorage pentru a stoca ultima sesiune pentru, să zicem, prima pagină. În esență, nu e o idee neapărat rea, pentru că m-ar oarecum scăpa de un nou API call când utilizatorul dă back în browser, dar ar trebui să scriu alte filtre și apar întrebări de genul:

  • dacă datele au fost salvate mai devreme de cinci minute, întreb sau nu serverul dacă a apărut ceva nou?
  • dacă au trecut cinci minute, totuși, și eu am mai publicat ceva, cum îi arăt că a apărut ceva nou? Dacă, totuși, n-am publicat nimic, nu tot trebuie să fac acel call oricum? Și ce fac dacă a apărut un text nou, totuși? Pe de o parte, React e suficient de rapid încât să randeze conținutul nou, dar asta înseamnă că o să-l mute pe cel vechi și se mișcă chestii pe ecran și eu ce fac? Păstrez poziția curentă în browser sau fac scroll la top conținutul către header?
  • pe de alta, dacă folosesc un placeholder care să semnalizeze că e în căutare de informații, utilizatorul se așteaptă să apară ceva, iar dacă nu apare, ce fac? Nu înseamnă că l-am mințit?

În principiu, astea nu-s neapărat niște probleme nemaipomenit de greu de rezolvat, cel puțin tehnic vorbind, dar sunt câteva întrebări care țin, până la urmă, de experiența utilizatorului și la care eu încă n-am găsit un răspuns concret. Bine, ar mai fi varianta unui service worker, dar… de ce mi-aș bate capul?

Altfel, avantaje ar fi destule pentru decuplarea frontend-ului de backend și migrarea pe React. Știu că în textul ăsta mai mult m-am plâns, dar sunt câteva chestii mișto:

  • scopul meu inițial era să separ backend-ul de frontend și de baza de date, chestie fezabilă dacă îți bați capul un pic ori cu Docker, ori cu mașini virtuale multiple
  • React e foarte mișto pentru chestii care implică un pic de interacțiune. Eu voiam să integrez un soi de reactions, ca pe facebook, atât la postările de pe blog, cât și la comentarii
  • din multe puncte de vedere, PHP-ul a devenit foarte frustrant pentru mine. Dau share la multe link-uri pe Twitter, de exemplu, și aș vrea să încep să fac asta pe blog. Aș vrea să fac un preview, în genul celui de pe facebook. Pentru browser, aș putea să scriu un “buton” pentru editor, nu e mare filozofie, care să tragă prin vreun API (există vreo trei servicii de care știu) datele care mă interesează. Din aplicația WordPress de mobil, n-am cum să fac asta. Cealaltă variantă ar fi ca serverul să proceseze query-ul înainte de publicare, dar… habar n-am să fac asta în PHP, iar curl nu e tocmai cea mai eficientă soluție
  • logica mea era să creez, de fapt, un PWA, pentru că, sub o formă sau alta, într-acolo ar cam trebui să ne îndreptăm cu toții când vine vorba de bloguri
  • există o serie de noi features precum hooks sau mai recentul Suspense care simplifică masiv munca de scriere a componentelor (deși astea două nu prea merg împreună; mă rog, Suspense cu useEffect), iar dacă-ți bați capul un pic și cu server-side rendering, tot aduni avantaje

În concluzie, am decis să abandonez proiectul pentru o vreme. De redesenat, redesenez oricum, dar o să mă întorc, temporar, la frustrările pe care mi le oferă cu generozitate PHP-ul.

În continuare cred că viitorul WordPress e pe React, dar nu mă aștept prea curând să văd o portare în JavaScript a core-ului WP. În vreun an sau doi, probabil că va exista și o astfel de opțiune, mai ales că pentru aplicații mari e ieftin să le rulezi cu Next.js pe servicii gen now.sh de la Zeit. Cred că vor exista două versiuni de WordPress, una în PHP, pentru ăia care nu vor să-și bată capul cu 30.000 fișiere pentru generarea a două HTML-uri statice, și una care va rula pe Node pentru nevoi mai diverse.

Altfel, nu țin morțiș să implementez toate ideile de mai sus, dar tind să cred că un pic mai mult control asupra propriului conținut e indicat pentru oricine postează relativ des. Și cum experiența facebook e aia cu care cei mai mulți oameni sunt obișnuiți, iar lui Zuck nu i-a fost niciodată rușine să fure de la alții, nu văd de ce astfel de funcții nu s-ar regăsi pe bloguri.

Am mai multe tâmpenii care-mi trec prin cap, apropo de asta, dar n-am resursele necesare (nici de timp, nici de bani) să fac ceva în sensul ăsta. Legat de blogul de față, o să încerc să implementez ce-oi nimeri folosindu-mă de jQuery, cel puțin până când oi avea timp să-mi bat capul cu o arhitectură mai deșteaptă în React.

9 comentarii

  1. Mă gândeam și eu anul trecut să trec modific tema de blog, să scot tot felul de extra scripturi și CSS files, să folosesc API-urile de la WP ca să fac ceva mai simplu și mai rapid. Deocamdată am parcat ideea. Lene, dar acum văd ca măcar cineva a făcut primii pași.

  2. Daca ma intrebai… :D
    E o o idee foarte buna. Endpoints poti genera cu wpgraphql si de acolo…sky is the limit. Ca sa iti fie usor si sa iti construiesti query-urile, foloseste graphiql.

    O sa pun pe git (la liber) o mini tema de blog – basic.

    PS: https://imgur.com/yaF7w6I
    :D :D

  3. Am facut cativa ani chestia asta, adica sa folosesc WP doar pentru backend, dar ma enerva ca era uneori extrem de lent, asa ca am migrat totul pe ghost. E mult mai rapid, cu interfata mai ok in admin si un api ceva mai prietenos.

  4. Câteva idei:

    – Cross orgin este o problemă doar dacă servești JS-ul de pe un domeniu iar API-ul este pe alt domeniu.
    – în WP, în loc de Curl poți folosi (e chiar recomandat!) wp_remote_get, respectiv wp_remote_post;
    – Treaba cu endpoint-urile care sunt lente uneori ar putea fi din cauza wp-cron (care face câte ceva mai intensinv la câteva minute). Îți recomand să dezactivezi wp-cron (ăla built in) și să-l pui într-un cronjob de linux.
    – Toate endpoint-urile existente în WP by default sunt gândite pentru integrarea cu Gutenberg & co, nu neapărat ca backend pentru site-uri. Poți defini foarte ușor endpoint-uri custom.
    – Caching-ul (pe frontend) nu este treaba endpoint-ului. Verificarea/actualizarea conținutului o poți face cu un worker, în fundal.

    Overall, dacă mergi pe PWA, pregătește-te să-ți codezi singur ORICE funcționalitate ce altfel ar fi disponibilă cu un plugin: lightbox pentru imagini, voturi pentru comentarii etc. Orice plugin interacționează cu frontend va trebui adaptat pentru ce ai :)

    • Păi, aveam nevoie să rezolv Cross Origin din cauză că, deși eram pe localhost și cu WP-ul și cu app-ul de React, tot făcea scandal. Nu știu de ce, porturi diferite, dracu’ să-l ia. Dar fix-ul ăla e bun de ținut undeva, că e o idee interesantă în eventualitatea în care poți să ții separat backendul de frontend pe mașini diferite. Anyway, mi-am creat vreo două endpoint-uri, nu-i bai.
      Legat de caching, nu mă refer la un cache propriu-zis, cît la evitarea unor rerender-uri din cauză că, odată ce-ai dat back, se produce un API call. Nu e neapărat o problemă tehnică, e mai degrabă filozofică și da, workers ar fi o soluție, doar că încă n-am ajuns acolo cu cititul, o să-i dau de cap la un moment dat.
      PS: nu mă deranjează în mod deosebit să-mi codez funcționalități, oricum nu folsoesc pluginuri ca să afișez chestii. N-am nici măcar lightbox activ.

  5. I so get what you’re saying, si eu m-am tot jucat cu api-ul de WP dar cu frontend in Vue.js, si chiar e o aventura :) Nu stiu cat te-ar ajuta, mai ales in punctul asta, dar mie mi-a fost destul de folositor ebook-ul de aici https://www.siteground.com/wordpress-rest-api-guide , si am zis sa il las ca poate il mai gaseste si altcineva folositor.

  6. offtopic

    Ai încercat Ghost? :)

    • Am mai fost întrebat despre asta pe facebook și cred că am mai avut o discuție similară și pe Twitter. Încă nu, dar parcă n-aș vrea să mă complic pînă cînd n-apuc să mă joc mai mult. Deocamdată, mă concentrez pe React, n-am un chef deosebit să mai învăț încă ceva.

  7. piate merita sa arunci o privire la Gatsby? (Dan Abramov lucrează la acest proiect) Are descris modul de integrare cu API wordpress, daca imi aduc bine aminte.. .