> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks

> Altere como o Aftersell Cart se comporta: transforme linhas, enriqueça-as com dados da Storefront, molde opções de assinatura e controle a adição ao carrinho.

Enquanto os [eventos](/pt/aftersell/cart/sdk-events) permitem *reagir* ao carrinho e as [ações](/pt/aftersell/cart/sdk-actions) permitem *alterá-lo*, os **hooks** mudam como o próprio carrinho se comporta: como as linhas são renderizadas, quais dados elas carregam e o que acontece na adição ao carrinho.

Os hooks ficam em `window.aftersell.cart.hooks`.

<Note>
  Um hook muda o que o comprador **vê**; uma ação muda o que está **no carrinho dele**. Ocultar uma linha de brinde com um transform a mantém no carrinho e no total. Removê-la com [`removeItem`](/pt/aftersell/cart/sdk-actions#removeitemkey) a tira de verdade.
</Note>

<Note>
  Hooks são chamadas de configuração, então é seguro registrá-los logo no topo do seu script, sem precisar esperar por `ready()`. Registre-os no script de **Initialization** do seu carrinho (veja [Scripts personalizados](/pt/aftersell/cart/custom-scripts)).
</Note>

<div id="how-registration-works">
  ## Como o registro funciona
</div>

Todo hook é um método `register*`. Você o chama com a sua função; ele retorna uma **função de desregistro** que você pode chamar para remover a sua.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);

// later: off();
```

O registro é **aditivo**, então a sua função roda ao lado de todas as outras. Isso importa porque o seu script raramente é o único na página: um app de assinatura, um app de bundle e o próprio tema podem todos se registrar no mesmo hook. Nenhum deles pode substituir o seu, e nada que você registrar pode ser silenciosamente descartado pelo que carregar depois de você.

| Hook                                                                                      | O que faz                                                                                                                                           | Com vários registros                                     |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| [`registerLineTransform`](#registerlinetransform)                                         | Oculta ou rerrotula linhas individuais.                                                                                                             | Todos rodam, em ordem de registro.                       |
| [`registerLineComparator`](#registerlinecomparator)                                       | Reordena as linhas renderizadas.                                                                                                                    | Compõem-se como critérios de desempate.                  |
| [`registerCartEnricher`](#registercartenricher)                                           | Anexa dados extras da Storefront a cada linha.                                                                                                      | Todos rodam; cada `id` é seu próprio namespace.          |
| [`registerSubscriptionOptionsTransform`](#registersubscriptionoptionstransform)           | Oculta ou renomeia os selling plans de uma linha.                                                                                                   | Todos rodam; os patches se mesclam por plano, por campo. |
| [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector) | Escolhe qual plano vem pré-selecionado.                                                                                                             | A primeira resposta não-`null` vence.                    |
| [`registerSkipAddToCartRule`](#registerskipaddtocartrule)                                 | Permite que formulários específicos ignorem o carrinho. Veja [Interceptação do adicionar ao carrinho](/pt/aftersell/cart/add-to-cart-interception). | Qualquer regra retornando `true` pula.                   |

Um hook que lança um erro, ou que não é uma função, é pulado; os demais ainda rodam, e o carrinho continua funcionando. Uma integração quebrada não consegue derrubar a adição ao carrinho, o seletor de assinatura ou a ordenação.

O outro lado é que um hook seu quebrado falha **silenciosamente**: nada chega ao console do navegador. Veja [Depuração](/pt/aftersell/cart/sdk-overview#debugging) para saber onde essas falhas aparecem.

***

<div id="registerlinetransform">
  ## registerLineTransform
</div>

`registerLineTransform(fn)` roda para cada linha do carrinho antes de ela ser renderizada. Use-o para ocultar uma linha ou mudar como ela é exibida, sem tocar no que realmente está no carrinho do comprador.

A função recebe uma linha somente leitura mais setters. Retorna uma função de desregistro.

| Setter                            | Efeito                                                                                                                                                    |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setHidden(bool)`                 | Oculta a linha do drawer. Ela continua no carrinho e no total.                                                                                            |
| `setTitle(string)`                | Muda o título exibido.                                                                                                                                    |
| `setVariantTitle(string \| null)` | Muda o rótulo de variante exibido.                                                                                                                        |
| `setInternalProperties(obj)`      | Mescla propriedades apenas de renderização. Nunca persistidas no Shopify. Usado para [agrupar linhas de bundle](/pt/aftersell/cart/sdk-use-case-bundles). |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Hide free gift lines from the drawer. The cart total is unaffected.
const off = window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) {
    line.setHidden(true);
  }
  if (line.sellingPlan) {
    line.setVariantTitle(`Delivered ${line.sellingPlan.name.toLowerCase()}`);
  }
});

// later: off();
```

<Warning>
  Um transform só muda o que é renderizado. Ele não pode mudar preço, quantidade ou identidade da linha. Use as [ações](/pt/aftersell/cart/sdk-actions) para isso.
</Warning>

**Use para:** ocultar linhas de brinde-com-compra ou injetadas por apps, rerrotular linhas de assinatura, marcar itens com desconto, ocultar componentes de bundle que o comprador não deveria gerenciar individualmente.

`setInternalProperties` é o setter por trás do agrupamento de bundles: carimbar as propriedades canônicas de bundle em cada linha é como você faz linhas separadas de um app de terceiros serem renderizadas como um único item. Veja [Agrupar linhas de bundle de outro app](/pt/aftersell/cart/sdk-use-case-bundles).

<div id="registerlinecomparator">
  ## registerLineComparator
</div>

Um comparator no mesmo formato que `Array.prototype.sort` espera. Ele roda depois de ocultar e renomear, então vê as linhas transformadas.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Subscriptions first, then everything else.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineB.sellingPlan ? 1 : 0) - (lineA.sellingPlan ? 1 : 0);
});
```

Comparators **compõem-se como critérios de desempate**: o primeiro a retornar um valor diferente de zero decide aquele par, e os demais são consultados apenas em empates. Retorne `0` para pares sobre os quais você não tem opinião. É isso que passa a decisão para o próximo comparator em vez de impor uma ordem a ele.

**Use para:** subir assinaturas ou itens de alto valor para o topo, afundar brindes e complementos para o final, manter um produto patrocinado em primeiro.

<div id="registercartenricher">
  ## registerCartEnricher
</div>

`registerCartEnricher(registration)` busca dados extras de produto ou variante na Storefront API do Shopify e os anexa a cada linha correspondente do carrinho em `line.metadata[id]`. Use-o para exibir metafields, tags ou qualquer outra coisa que a Storefront API exponha, sem necessidade de alteração de código pelo Aftersell.

| Campo      | Tipo                              | Descrição                                                                                                                  |
| ---------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `id`       | `string`                          | Namespace do resultado; ele fica em `line.metadata[id]`. Deve ser único; um segundo registro com o mesmo `id` é ignorado.  |
| `onType`   | `'Product'` ou `'ProductVariant'` | Qual nó o fragment alveja. Também é a chave de junção (ID de produto vs. ID de variante).                                  |
| `fragment` | `string`                          | Uma seleção de campos GraphQL (sem chaves externas) inserida na query da Storefront. As chaves precisam estar balanceadas. |

Retorna uma **função de desregistro**.

Sempre que o carrinho carrega ou muda, o Aftersell busca o seu fragment para cada produto ou variante no carrinho e anexa o resultado. A busca não é bloqueante: o carrinho é renderizado imediatamente e reemite `cart_updated` quando os dados chegam. Um fragment lento ou com falha nunca atrasa nem quebra o carrinho.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerCartEnricher({
  id: 'pricing',
  onType: 'ProductVariant',
  fragment: `
    anchorPrice: metafield(namespace: "custom", key: "anchor_price") { value }
    subscriberPrice: metafield(namespace: "custom", key: "subscriber_price") { value }
  `,
});

// Read it once the data arrives.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    const anchor = line.metadata.pricing?.anchorPrice;
    if (anchor) console.log(line.title, 'anchor price', anchor.value);
  });
});
```

Como o enriquecimento é assíncrono, sempre proteja a leitura, já que `line.metadata.pricing` é `undefined` até a primeira busca resolver, e `metadata` em si tem `{}` como padrão.

**Use para:** puxar um metafield para cada linha (uma estimativa de entrega, uma lista de ingredientes, uma flag de "enviado separadamente", um multiplicador de fidelidade) e renderizá-lo por meio de um [bloco Custom code](/pt/aftersell/cart/custom-code-blocks). Veja [exibindo dados de metafield nas linhas do carrinho](/pt/aftersell/cart/sdk-use-case-metafields).

<Note>
  Vários enrichers coexistem tranquilamente, já que cada `id` é seu próprio namespace, então os dados nunca colidem.
</Note>

<Warning>
  Valores enriquecidos são retornados como estão da Storefront API e **não** são sanitizados. Renderize-os como texto, não como HTML bruto.
</Warning>

<div id="registersubscriptionoptionstransform">
  ## registerSubscriptionOptionsTransform
</div>

Oculte ou renomeie os selling plans oferecidos em uma linha. Sua função recebe opções somente leitura mais setters, e não retorna nada.

| Setter            | Efeito                        |
| ----------------- | ----------------------------- |
| `setHidden(bool)` | Oculta o plano do seletor.    |
| `setName(string)` | Muda o nome exibido do plano. |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSubscriptionOptionsTransform((options, context) => {
  // context: { productId, variantId }
  options.forEach((option) => {
    if (option.discountPercent === 0) option.setHidden(true);
    option.setName(option.name.replace('Every ', ''));
  });
});
```

**Setters, e não uma lista retornada, para que vários scripts possam coexistir.** Se este hook retornasse um array, um transform que só se importa com um plano naturalmente escreveria `options.filter(...)` e apagaria silenciosamente os planos de todos os outros apps no caminho. Com setters você só consegue descrever as suas próprias edições: os patches se mesclam por plano e por campo, e o último a escrever vence um conflito genuíno no mesmo campo do mesmo plano. Um transform que lança um erro não contribui com nada, e os outros ainda se aplicam.

Todo transform vê as opções *originais*, não uma visão parcialmente aplicada, então a ordem de registro não muda o que você está lendo.

<Note>
  A ordem dos planos permanece como o Shopify retornou, então um transform não pode reordenar. Para controlar qual plano é oferecido primeiro (e a qual plano o botão de upgrade de compra única assina), use [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector), que promove a escolha dele para a frente.
</Note>

Você também não pode *adicionar* um plano nem mudar um preço: `discountPercent` não tem setter, porque um plano que o Shopify não honraria no checkout seria só uma promessa quebrada no seletor.

<div id="registerdefaultsubscriptionoptionselector">
  ## registerDefaultSubscriptionOptionSelector
</div>

Escolha qual plano vem pré-selecionado em uma linha. Retorne um `id` de plano, ou `null` para passar a vez.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerDefaultSubscriptionOptionSelector((options) => {
  const best = options
    .slice()
    .sort((optionA, optionB) => optionB.discountPercent - optionA.discountPercent)[0];
  return best ? best.id : null;
});
```

O **primeiro selector a retornar o id de um plano disponível vence**, então retorne `null` para as linhas com que você não se importa em vez de adivinhar. Isso passa a decisão para o próximo selector em vez de sobrescrevê-la. Um id que não corresponde a nenhum plano na linha é tratado como `null` e também cede a vez, então um id obsoleto não consegue zerar o seletor.

Sua função recebe `(options, context)`, o mesmo `context` que o transform de opções recebe.

<div id="registerskipaddtocartrule">
  ## registerSkipAddToCartRule
</div>

Retorne `true` para deixar um formulário de produto específico adicionar ao carrinho normalmente, ignorando o Aftersell por completo. Isso é útil para um formulário que precisa do próprio redirecionamento ou tratamento.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);
```

**Qualquer `true` pula**, então mantenha sua regra restrita, correspondendo aos formulários específicos que você controla, e retorne `false` para todo o resto. As regras são avaliadas em ordem de registro e param no primeiro `true`, então não coloque efeitos colaterais em uma: se a sua roda ou não depende do que foi registrado antes dela.

<Tip>
  Se você controla a marcação do formulário, nem precisa de um hook: adicione a classe **`aftersell-cart-skip-atc`** ao `<form>` e o Aftersell o deixa em paz. Use este hook quando você não pode editar a marcação, ou quando a decisão depende de algo que só o seu código sabe.
</Tip>

**Use para:** um formulário de pré-venda ou orçamento que precisa do próprio redirecionamento, o fluxo personalizado de um app de assinatura, um botão de "comprar agora" que deve ir direto para o checkout. Para desativar a interceptação na página inteira, use [`skip_add_to_cart_interceptor`](/pt/aftersell/cart/sdk-configure#skip_add_to_cart_interceptor), mas prefira este hook, que é limitado aos formulários que você nomeia.

<div id="where-to-go-next">
  ## Para onde ir agora
</div>

* **[Objeto de carrinho](/pt/aftersell/cart/sdk-cart-object)**: o formato da linha que um transform recebe.
* **[Eventos](/pt/aftersell/cart/sdk-events)**: tudo o que você pode assinar.
* **[Ações](/pt/aftersell/cart/sdk-actions)**: lendo e alterando o carrinho.
* **[Casos de uso](/pt/aftersell/cart/sdk-use-cases)**: soluções completas para pedidos comuns.
