Saltar al contenido
Meeko
Escribe para buscar en las guías y en la referencia de la API. Ábrela desde cualquier página con / o Ctrl K; muévete con las flechas y entra con Intro.
Empezar

Contenidos

En esta página

createContext

Crea una clave única y tipada para compartir un valor con los descendientes mediante provide e inject.

reactividad Desde 1.0.0 Sin import @meekojs/meeko Código fuente

Se autoimporta en las aplicaciones del framework.

ts
import { createContext } from "@meekojs/meeko";

createContext<T>(description?) crea la clave con la que un componente publica un valor (provide) y sus descendientes lo leen (inject). La clave es un Symbol nuevo, así que dos contextos nunca chocan aunque tengan la misma descripción, y lleva el tipo T del valor: inject(clave) devuelve T sin conversiones.

Crea la clave una vez, en un módulo, y expórtala junto a las funciones que la usan. Se exporta desde @meekojs/meeko y se autoimporta en las aplicaciones del framework.

Uso#

ts
// app/composables/theme.ts
import { createContext, inject, provide, signal } from "@meekojs/meeko";
import type { Signal } from "@meekojs/meeko";

type Theme = "light" | "dark";
const themeKey = createContext<Signal<Theme>>("theme");

export function provideTheme(initial: Theme): Signal<Theme> {
  const theme = signal<Theme>(initial);
  provide(themeKey, theme);
  return theme;
}

export const useThemeContext = (): Signal<Theme> => inject(themeKey);

Un layout llama a provideTheme("light") en su script; cualquier componente dentro llama a useThemeContext().

Tipo#

ts
function createContext<T>(description?: string): ContextKey<T>

ContextKey

ts
type ContextKey<T> = symbol & {
	readonly __meekoValue?: (value: T) => T
}

Parámetros#

Parámetro Tipo Por defecto Descripción
T parámetro de tipo unknown Tipo del valor que se publica con la clave. Escríbelo siempre: no se infiere de ningún valor.
description string ninguna Texto del Symbol. Aparece en el error de inject cuando falta el proveedor: Missing Meeko context: theme.

Valor devuelto#

Una ContextKey<T>: un symbol con una marca de tipos que fija T. La marca sólo existe en TypeScript.

Ejemplos#

Un contexto con su provide y su inject tipados#

ts
// app/composables/toasts.ts
import { createContext, inject, provide, signal } from "@meekojs/meeko";
import type { ReadonlySignal } from "@meekojs/meeko";

interface Toasts {
  readonly items: ReadonlySignal<readonly string[]>;
  show(message: string): void;
}

const toastsKey = createContext<Toasts>("toasts");

export function provideToasts(): Toasts {
  const items = signal<readonly string[]>([]);
  const toasts: Toasts = { items, show: (message) => { items.value = [...items.value, message]; } };
  provide(toastsKey, toasts);
  return toasts;
}

export const useToasts = (): Toasts => inject(toastsKey);

Un contexto con valor por defecto#

inject falla si nadie publicó la clave. Para un contexto opcional, publica el valor por defecto en la raíz de la aplicación (app/app.meeko), de modo que todo el árbol lo tenga:

meeko
<!-- app/app.meeko -->
<script lang="ts">
import { provideToasts } from "./composables/toasts";
provideToasts();
</script>

<template><div class="site-root"><MeekoOutlet /></div></template>

Un componente compuesto#

Un <Tabs> publica su estado y cada <Tab> lo lee, sin pasar props por cada nivel:

ts
// app/components/tabs-context.ts
import { createContext } from "@meekojs/meeko";
import type { Signal } from "@meekojs/meeko";

export const tabsKey = createContext<{ active: Signal<string>; select(id: string): void }>("tabs");
meeko
<!-- app/components/Tab.meeko -->
<script lang="ts">
import { tabsKey } from "./tabs-context";
const props = defineProps<{ id: string; label: string }>();
const tabs = inject(tabsKey);
</script>

<template>
  <button type="button" role="tab" bind:aria-selected="String(tabs.active.value === props.id)" on:click="() => tabs.select(props.id)">{props.label}</button>
</template>

Errores comunes#

Crear la clave dentro del componente#

Cada ejecución del script crearía un Symbol distinto, que los descendientes nunca encuentran.

Incorrectomeeko
<script lang="ts">
const panelKey = createContext<string>("panel");
provide(panelKey, "lateral");
</script>

Un hijo que crea su propia clave con createContext<string>("panel") y la inyecta lanza Error: Missing Meeko context: panel: la descripción es la misma, pero el símbolo no. Crea la clave una vez, en un módulo, e impórtala en los dos lados:

Correctots
// app/components/panel-context.ts
import { createContext } from "@meekojs/meeko";
export const panelKey = createContext<string>("panel");

Sustituir la clave por Symbol.for#

Incorrectots
export const modalKey = Symbol.for("modal") as ContextKey<ModalContext>;

Symbol.for devuelve el mismo símbolo a todo el proceso: dos librerías, o una librería y la aplicación, que usen "modal" comparten la clave sin saberlo, y una lee el valor de la otra. Usa createContext y deja que Meeko garantice que el módulo de la clave se evalúa una sola vez (véase Notas):

Correctots
export const modalKey = createContext<ModalContext>("modal");

Olvidar el tipo#

Incorrectots
const userKey = createContext("user");
const user = inject(userKey);
user.name; // TS18046: 'user' is of type 'unknown'

Sin parámetro de tipo, la clave es ContextKey<unknown> y todo lo que lee inject es unknown. Declara el tipo en la clave:

Correctots
const userKey = createContext<{ id: string; name: string }>("user");

Publicar un valor de otro tipo#

Incorrectots
const localeKey = createContext<string>("locale");
provide(localeKey, 1);

meeko check da TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

Correctots
provide(localeKey, "es");

Notas#

  • Una sola evaluación del módulo. La clave es el valor de una evaluación concreta del módulo que la crea: el proveedor y los consumidores se encuentran mientras importen esa misma evaluación. Meeko lo garantiza:

    • con el autoimport y con un import relativo: modalKey autoimportado desde app/utils/modal.ts e import { modalKey } from "../utils/modal" son el mismo módulo, en el servidor y en el navegador;
    • en meeko dev, con un plugin que provee la clave: editar el módulo de la clave, el plugin o cualquier módulo que importe un plugin o server/runtime.ts crea de nuevo la app de servidor, así que el plugin publica la clave que leen las páginas. Editar una página o un componente, y lo que sólo ellos importan, es una actualización en caliente que conserva la app; una recarga completa (editar un archivo de app/utils, añadir un componente) también la conserva, con los módulos que importan sus plugins;
    • con un módulo de Meeko instalado en node_modules: su runtime llega por el nombre del paquete (sus autoimports) y por la ruta de sus archivos (addPlugin, addComponentsDir), y Meeko compila esos paquetes como código fuente (ssr.noExternal y optimizeDeps.exclude), así que el plugin del módulo y las páginas comparten la clave en meeko dev y tras meeko build.

    Cada copia del módulo crea su propia clave: si la clave se crea dentro del <script> de un componente (véase Crear la clave dentro del componente), o si una librería se instala dos veces, en versiones distintas, y cada parte del árbol importa una.

  • Servidor y navegador. La clave es un valor normal de módulo, igual en los dos. Lo que se publica con ella vive en la raíz de cada componente: en SSR es por petición, y no se mezcla entre usuarios.

  • Descripción. Úsala siempre: es lo único que nombra el contexto en el error de inject.

  • Contexto o estado. El contexto sigue el árbol de componentes y no viaja en el payload. Para estado que debe ser el mismo en todo el árbol y transferirse del servidor al navegador, usa useState.

Relacionado#

¿Te sirvió esta página?

Añadir un comentario

Se envía al elegir «Sí» o «No». Solo se guardan la página, tu respuesta y el comentario: ni cookies ni datos tuyos.

0 de 2000 caracteres

Editar en GitHub: docs/reference/reactivity/createcontext.md