Update fetch for static page gen, fix DOM patching
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Client-side fetch helpers — the counterparts to RespondGob / RespondJSON. In a
|
||||
@@ -12,24 +11,33 @@ import (
|
||||
// the server encodes with RespondGob(data) and the client decodes straight back
|
||||
// into that type with FetchGob — no JSON, no hand-written unmarshalling. These
|
||||
// are neutral (they only touch encoding/*), so app code can call them while
|
||||
// staying SSR-able; the actual HTTP transport is client-only and injected at
|
||||
// staying SSR-able; the actual HTTP transport is client-only and installed at
|
||||
// startup by the wasm runtime (SetClientTransport), the inversion vdom.Schedule
|
||||
// uses.
|
||||
|
||||
// clientTransport does an HTTP GET returning the raw body; nil on the server.
|
||||
// clientTransport does an HTTP GET returning the raw body. The wasm runtime
|
||||
// installs it in its init, so it is always set in the browser and always nil
|
||||
// everywhere else — "nil transport" therefore means "not the client", which is
|
||||
// what makes the SSR behavior in fetchInto safe.
|
||||
var clientTransport func(url string) ([]byte, error)
|
||||
|
||||
// SetClientTransport installs the HTTP GET used by FetchGob / FetchJSON. The
|
||||
// wasm client runtime calls this at startup with wasmruntime.FetchBytes.
|
||||
// wasm client runtime installs wasmruntime.FetchBytes automatically; call this
|
||||
// only to wrap it (auth headers, a base URL, a test double).
|
||||
func SetClientTransport(get func(url string) ([]byte, error)) { clientTransport = get }
|
||||
|
||||
func fetchInto[T any](url string, cb func(T, error), decode func([]byte, *T) error) {
|
||||
// Off the client (SSR of a static page, a native build) there is no transport
|
||||
// and no browser to fetch with, so the request simply never happens: cb is not
|
||||
// called, and the caller stays in the loading state it started in. That is
|
||||
// what SSR should ship — a spinner or skeleton, not a "fetch failed" error the
|
||||
// server was never able to avoid. The client re-runs the fetch for real on its
|
||||
// first render after hydration.
|
||||
if clientTransport == nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
var out T
|
||||
if clientTransport == nil {
|
||||
cb(out, errors.New("httputil: no client transport installed (client-only)"))
|
||||
return
|
||||
}
|
||||
body, err := clientTransport(url)
|
||||
if err != nil {
|
||||
cb(out, err)
|
||||
@@ -51,6 +59,10 @@ func fetchInto[T any](url string, cb func(T, error), decode func([]byte, *T) err
|
||||
// httputil.FetchGob("/api/quotes", func(qs []Quote, err error) {
|
||||
// if err != nil { /* … */ } else { quotes.Set(qs) }
|
||||
// })
|
||||
//
|
||||
// Fetching is client-only: during SSR this does nothing and cb is never called,
|
||||
// so a page that starts out loading renders its spinner into the static HTML and
|
||||
// fetches for real once the client takes over.
|
||||
func FetchGob[T any](url string, cb func(T, error)) {
|
||||
fetchInto(url, cb, func(b []byte, out *T) error {
|
||||
return gob.NewDecoder(bytes.NewReader(b)).Decode(out)
|
||||
|
||||
Reference in New Issue
Block a user