Saltar al contenido principal

SDK Helper

Las apps NubeSDK se ejecutan dentro de un web worker aislado, sin acceso directo al DOM. Todo lo que hace la app: leer el estado, renderizar componentes, navegar y almacenar datos pasa por la instancia del SDK que el runtime entrega al punto de entrada App(nube).

En la práctica, esa instancia nube termina pasando por todas las funciones y componentes de la app, y algunos patrones se repiten en cada proyecto: leer la página actual, estrechar tipos de página, renderizar un componente por producto, mostrar un toast al recibir un evento.

@tiendanube/nube-sdk-helper concentra esos patrones en un conjunto de utilidades pequeño y fuertemente tipado.

Instalación

npm install @tiendanube/nube-sdk-helper @tiendanube/nube-sdk-types

@tiendanube/nube-sdk-types es una peer dependency y debe instalarse junto con el paquete helper.

Registrar la instancia

El runtime entrega la instancia del SDK solo como argumento del punto de entrada. La idea central del helper es simple: registrá una vez, y todos los demás helpers llegan a ella por su cuenta, sin necesidad de pasar nube por toda la jerarquía de la app.

src/App.ts
import {
setNubeInstance,
getCurrentState,
ui,
} from "@tiendanube/nube-sdk-helper";
import type { NubeSDK } from "@tiendanube/nube-sdk-types";

export function App(nube: NubeSDK) {
setNubeInstance(nube); // call this first, before anything else

const state = getCurrentState();
ui.showToast(`You are on the ${state.location.page.type} page`);
}

Tres funciones gestionan la instancia:

  • setNubeInstance(nube): registra la instancia (llamá una vez al inicio del App).
  • getNubeInstance(): devuelve la instancia registrada, lanzando un error descriptivo si aún no fue registrada.
  • clearNubeInstance(): limpia la instancia (útil en tests).

Por qué esto importa

Con la instancia registrada globalmente, las acciones más comunes se convierten en funciones independientes que podés llamar desde cualquier lugar: un componente anidado, un módulo utilitario, un event handler, sin necesidad de recibir nube como parámetro.

import { navigate, ui } from "@tiendanube/nube-sdk-helper";

// En cualquier parte de la app, sin `nube` en el scope:
function onCheckoutClick() {
navigate("/checkout"); // routes to the path internally via the SDK instance
ui.showToast("Taking you to checkout...", "info");
}

Sin el helper, sería necesario tener una referencia a nube en el scope y llamar a nube.getBrowserAPIs().navigate(...), además de construir el componente de toast a mano. El helper reduce ambos a una sola línea.

Lo mismo aplica para el almacenamiento en el browser:

import { browser } from "@tiendanube/nube-sdk-helper";

await browser.asyncLocalStorage.setItem("seen-banner", "true");
const seen = await browser.asyncLocalStorage.getItem("seen-banner");

Leer el estado con selectores

Los selectores siguen un patrón consistente: llamados sin argumento, leen el estado actual del SDK; pasando un estado explícito, se convierten en funciones puras.

import { getCartItems, getPageType, getCustomer } from "@tiendanube/nube-sdk-helper";

// Sin argumento: lee el estado actual de la instancia del SDK.
const items = getCartItems();
const pageType = getPageType();
const customer = getCustomer();

// Con estado explícito: puro, ideal para tests unitarios.
const itemsFromMock = getCartItems(mockState);
Testeabilidad

Pasá un estado mock a cualquier selector y se comporta como una función pura: sin efectos secundarios, sin depender de la instancia registrada. Este patrón aplica a toda la familia de selectores.

Guards

Los guards cumplen una doble función: validan en runtime y estrechan el tipo para TypeScript, desbloqueando los datos específicos de la página tipados por el compilador.

import { getCurrentState, isProductPage } from "@tiendanube/nube-sdk-helper";

const { page } = getCurrentState().location;

if (isProductPage(page)) {
// `page` is now narrowed to a ProductPage, so `page.data.product` is typed.
console.log(page.data.product.name);
}
Validación y tipado juntos

A diferencia de un cast (as ProductPage), los guards verifican la estructura en runtime antes de estrechar el tipo. Si la condición no pasa, TypeScript no expone page.data.product.

Hay un guard para casi toda estructura encontrada en una app NubeSDK:

  • Páginas: isProductPage, isCategoryPage, isCheckoutPage, isHomePage, isAllProductsPage, isSearchPage
  • Carrito: isCart, isCartItem, isCartValidationSuccess, isCartValidationPending, isCartValidationFail
  • Dominio: isStore, isCustomer, isPayment, isShipping, isAddress, y más
  • Componentes / datos de página: isNubeComponent, hasProductList, hasSections, hasSingleProduct, isSectionWithProducts

Getters

Además de los selectores de estado, los getters exponen los metadatos inyectados por el runtime sobre la app. Uno útil es getScriptURL, que analiza la URL desde donde se cargó el script de la app (cacheada como una URL congelada):

import { getScriptURL, getScriptParam } from "@tiendanube/nube-sdk-helper";

const url = getScriptURL();
console.log("Script origin:", url.origin);
console.log("Script pathname:", url.pathname);

// Read configuration passed as query params on the script URL, e.g. ?variant=b
const variant = getScriptParam("variant"); // string | null

Esta es la forma idiomática de configurar una app desde la tag de script, sin necesidad de una solicitud adicional.

Page matching

pageMatch y onPage resuelven el mismo problema desde dos ángulos. pageMatch despacha una vez contra un estado provisto, y cada handler recibe el payload correctamente tipado para su página. onPage envuelve pageMatch, pero suscribe a la navegación, re-ejecutándose en cada cambio de página y devolviendo una función de cancelación.

Usá pageMatch para una decisión puntual basada en el estado actual:

import { pageMatch, getCurrentState } from "@tiendanube/nube-sdk-helper";

// Runs once against the state you pass in.
pageMatch(getCurrentState(), {
product: (state, product) => console.log("Product:", product.name),
checkout: (state, checkout) => console.log("Step:", checkout.step),
});

Usá onPage para reaccionar continuamente a la navegación del usuario:

import { onPage } from "@tiendanube/nube-sdk-helper";

const stop = onPage({
product: (state, product) => trackProductView(product.id),
checkout: (state, checkout) => {
if (checkout.step === "success") trackPurchase();
},
// category / home handlers are optional
});

// Later, when you no longer need it:
stop();

Para checkout específicamente, onCheckoutStep permite reaccionar a un step particular:

import { onCheckoutStep } from "@tiendanube/nube-sdk-helper";

onCheckoutStep({
success: () => trackPurchase(),
});

Render

Una necesidad frecuente es renderizar algo en un slot de grilla de producto: un badge, un label, un ícono. forEachProduct extrae todos los productos del estado actual (independientemente del tipo de página), los mapea a través de una factory de renderizado, descarta resultados vacíos y asigna automáticamente un key único a partir del id del producto.

import { getNubeInstance, forEachProduct } from "@tiendanube/nube-sdk-helper";

getNubeInstance().render(
"product_grid_item_image_bottom_right",
forEachProduct((product) => <MyBadge product={product} />),
);

Sin JSX, la factory devuelve un objeto de componente directamente:

getNubeInstance().render(
"product_grid_item_image_bottom_right",
forEachProduct((product) => ({ type: "txt", children: product.name })),
);

Devolver null o undefined desde la factory omite el producto: esas entradas se filtran automáticamente.

Helpers de UI y eventos

ui agrupa las operaciones de vista más comunes, incluyendo renderizar el mismo componente en múltiples slots en una sola llamada:

import { ui } from "@tiendanube/nube-sdk-helper";

ui.renderAll(["corner_top_left", "corner_top_right"], {
type: "txt",
children: "Hi",
});
ui.showToast("Done!", "success");
ui.clear("corner_top_right");

onEvent y toastOn reducen el patrón repetitivo de "escuchar y reaccionar" a una línea cada uno, ambos devolviendo una función de cancelación:

import { onEvent, toastOn } from "@tiendanube/nube-sdk-helper";

const off = onEvent("cart:update", (state) => {
console.log("items:", state.cart.items.length);
});

// Later, when you no longer need it:
off();

toastOn("cart:add:success", "Added to cart", "success");
toastOn("cart:update", (state) => `Cart: ${state.cart.items.length} items`);

Próximos pasos

  • Events — Lista completa de eventos disponibles en NubeSDK
  • State — Estructura de estado de NubeSDK
  • UI Slots — Slots disponibles para renderizado

Help us improve NubeSDK

Found an issue or have a suggestion? Let us know on GitHub.