Inertia.js, React a Django

Po přechodu na single-page aplikaci (přibližně před rokem) se naše produktivita nezlepšila i přesto, že jsme tým prakticky zdvojnásobili. Říkal jsem si, že se to časem srovná. K přechodu nás donutil vývoj okolo 3rd party cookies, protože naše aplikace běží v iframe v administraci e-shopu.

Pak přišel Hotwire od lidí okolo Rails, všimnul jsem si HTMX. Tyto knihovny používají HTML over the wire přístup, který pro nás není vhodný. A proto mě zaujala Inertia.js, která se od nich odlišuje. Snaží se totiž definovat protokol, jak by spolu měli React, Vue nebo Svelte aplikace a backend komunikovat. Díky tomu můžeme vytvářet JavaScriptové SPA bez nutnosti psát API a client-side routování. Na serveru jsou oficiálně podporované Laravel a Rails. Pro Django existuje neoficiální balíček.

Inertia funguje velmi podobně jako klasická webová aplikace. Stále píšeme controllery, čteme data pomocí ORM a renderujeme views. V případě Inertia jsou ale views JavaScriptové komponenty. Takové řešení vytváří velmi úzkou provázanost mezi backendem a frontendem, což je v případě klasických MVC aplikací běžná praxe a podle autora knihovny i jejich benefit.

Tento přístup je pro nás zajímavý hlavně kvůli Polaris - Design Systému od Shopify. Většinu HTML a CSS si nepíšeme sami, ale používáme React komponenty připravené Shopify.

Ve view pouze definuju, jaká komponenta se má použít a jaké props dostane. render_inertia rozhodne, jestli vrátí JSON nebo HTML odpověď.

def home(request):
    props = {
        "events": [{
            "id": 1,
            "title": "Title",
            "description": "description, wow"
        }],
    }
    return render_inertia(request, "Home", props)

Pro přechod mezi stránkami existuje komponenta Link. Díky ní Inertia načte pouze data a nedochází k full-page refresh. Ineria totiž pošle požadavek s hlavičkou X-Inertia. Server v takovém případě pošle JSON místo HTML.

Nedílnou součástí každé aplikace jsou formuláře. Inertia proto nabízí helper funkce pro všechny podporované frameworky.

Samotný kód vypadá následovně. V případě Reactu má hook useForm na starosti uchování stavu, odeslání dat na server a validace. Způsob zpracování požadavku je velmi podobný klasickému odeslání formuláře. Pokud je potřeba uživateli zobrazit validační hlášku, stačí do props přidat klíč errors. Inertia helper useForm potom zpřístupní pomocí stejnojmenného klíče.

import React from 'react'
import { Link, useForm } from '@inertiajs/inertia-react'

export default function Create() {
    const { data, setData, post, processing, errors } = useForm({
        email: '',
        password: '',
        remember: false,
    })

    function submit(e) {
        e.preventDefault()
        post('/inertia/create')
    }

    return (
        <div>
<h2>Create event</h2>
<link href="/inertia"/>Back
            <form onsubmit="{submit}">
<input =="" onchange="{e" type="text" value="{data.email}"/> setData('email', e.target.value)} />
                {errors.email &amp;&amp; <div>{errors.email}</div>}
                <input =="" onchange="{e" type="password" value="{data.password}"/> setData('password', e.target.value)} />
                {errors.password &amp;&amp; <div>{errors.password}</div>}
                <input =="" checked="{data.remember}" onchange="{e" type="checkbox"/> setData('remember', e.target.checked)} /> Remember Me
                <button disabled="{processing}" type="submit">Login</button>
</form>
</div>
    )
}

Zpracování POST requestu na serveru.

from inertia.views import render_inertia
from inertia.share import share, share_flash

def event_create(request):
    if request.method == "POST":
        data = json.loads(request.body)
        if not data["email"]: # Just so there's some validation
            share_flash(request, error=True, errors={"email": ["email can't be empty"]})
        else:
            share_flash(request, success=f"event {data['email']} created")
            return redirect(reverse("inertia_react:index"))

    return render_inertia(request, "Events.Create", {})

Inertia představuje velmi tenkou vrstvu mezi tradičními webovými frameworky a moderními frontendovými frameworky. Možnost použít React místo klasických šablon bez nutnosti vybudovat nejdříve API, je pro mě velmi lákavá. Nemusí to být ani konečný stav. Inertia může být krok mezi jednotlivými komponentami a SPA.

Momentálně převádíme jednu aplikaci na Inertia a zatím z toho mám dobrý pocit. Jsem velmi zvědavý, jak se osvědčí.