Inspeccionar y manipular los hijos resueltos
Inspeccionar y transformar los hijos ya resueltos: convertirlos en array con toArray, filtrarlos por tipo, envolver cada uno y mutar los nodos desde un efecto.
Una vez que children() te entrega los hijos como un valor estable, se abre un taller entero: puedes contarlos, filtrarlos, envolver cada uno en un contenedor o incluso mutar sus nodos directamente. Es un poder que React reserva a React.Children y cloneElement, pero que en Solid opera sobre nodos DOM reales, con reglas propias. Este nivel es el manual de ese taller.
- Pasar de
resueltos()a un array manejable contoArray(). - Filtrar y contar hijos por tipo (
HTMLElement, texto, nulos). - Envolver cada hijo en un contenedor con
<For>omap. - Mutar los nodos resueltos desde un
createEffect, con seguridad.
De getter a array
resueltos.toArray() es tu punto de entrada. Devuelve un array de ResolvedJSXElement: la mezcla real de lo que hay entre las etiquetas —elementos DOM, cadenas, números, null—. No asumas que todo son elementos.
Un matiz para iterar: como children() memoiza, las referencias que devuelve toArray() son estables entre lecturas mientras los hijos no cambien. Por eso <For>, que reconcilia por identidad de referencia, empareja cada nodo consigo mismo sin recrearlo. Es lo contrario de leer props.children crudo, donde cada acceso daría nodos nuevos y <For> los trataría como elementos distintos en cada pasada.
toArray() es también el punto donde tipas: su retorno es ResolvedJSXElement[], y un filter con guarda de tipo —(n): n is HTMLElement => ...— estrecha esa unión para que TypeScript te deje tocar .style sin quejarse. Inspeccionar hijos en Solid es, en buena medida, un ejercicio de estrechar tipos sobre una colección heterogénea.
import { children, createEffect } from "solid-js";
const resueltos = children(() => props.children);
createEffect(() => {
const lista = resueltos.toArray();
console.log("cantidad:", lista.length);
console.log("tipos:", lista.map((n) => typeof n));
});
mindmap
root((hijos resueltos))
Leer
resueltos getter
toArray lista
Filtrar
instanceof HTMLElement
por dataset
Envolver
For con li
map estatico
Mutar
classList add
style desde efectoFiltrar y contar
Como la lista es heterogénea, casi siempre querrás quedarte solo con los elementos. Un predicado con guarda de tipo lo deja limpio y tipado:
const soloElementos = () =>
resueltos.toArray().filter(
(n): n is HTMLElement => n instanceof HTMLElement,
);
A partir de ahí, filtrar por un criterio es trivial: por dataset, por atributo, por clase.
const visibles = () =>
soloElementos().filter((el) => el.dataset.oculto !== "true");
Este filtrado no es un ejercicio de estilo: es la base de los compound components que leen metadatos de sus hijos. Un Tabs puede extraer el título de cada panel buscando el.dataset.titulo sobre los elementos resueltos, en vez de exigir una prop aparte. Es más frágil que context —lo veremos en 8.4—, pero es una técnica legítima cuando los hijos son elementos planos cuya forma controlas.
Si escribes <Comp>hola {nombre()} mundo</Comp>, entre las etiquetas hay cadenas y quizá números, no elementos. Llamar a .style o .classList sobre ellos revienta. Filtra por instanceof HTMLElement antes de tocar cualquier API del DOM. En SSR, además, algunos nodos pueden no ser HTMLElement sino representaciones en cadena: prueba tu manipulación también en el servidor.
Envolver cada hijo
Para decorar la estructura —envolver cada hijo en un <li>, una tarjeta, una celda— itera el array y compone nuevo JSX alrededor de cada nodo. Con <For> obtienes reconciliación por referencia y reactividad si los hijos cambian:
function Menu(props: { children: JSX.Element }) {
const items = children(() => props.children);
return (
<ul role="menu">
<For each={items.toArray()}>
{(item) => <li role="menuitem" class="item">{item}</li>}
</For>
</ul>
);
}
Cada item es el nodo ya resuelto; insertarlo dentro del <li> funciona porque lo colocas en un sitio. Para listas estáticas, un map directo en el JSX es igual de válido y más escueto; reserva <For> para cuando el conjunto de hijos varíe en el tiempo.
{items.toArray().map(...)} colocado dentro del JSX no es una foto estática: al leer items() rastrea el memo, así que se re-evalúa si los hijos cambian. La diferencia con <For> no es la reactividad, sino la reconciliación: map reconstruye la lista de envoltorios en cada cambio, mientras <For> conserva los que siguen. Para pocos hijos da igual; para muchos o con estado interno, prefiere <For>.
Mutar los nodos resueltos
Como tienes nodos DOM de verdad, puedes modificarlos en el sitio. Hazlo siempre dentro de un createEffect: así corre tras el montaje, se re-aplica si los hijos cambian y participa en la limpieza de propiedad (ownership) del componente.
function ListaColor(props: { color: string; children: JSX.Element }) {
const resueltos = children(() => props.children);
createEffect(() => {
resueltos.toArray().forEach((n) => {
if (n instanceof HTMLElement) n.style.color = props.color;
});
});
return <>{resueltos()}</>;
}
Al leer props.color y resueltos() dentro del efecto, este se re-ejecuta cuando cambia el color o cuando cambian los hijos, re-pintando exactamente lo necesario. Es imperativo, sí, pero acotado y reactivo. Es también el patrón canónico del ColoredList de la documentación de Solid: mutar el DOM resuelto desde un efecto que sigue las props.
Si un atributo también lo controla un binding reactivo del propio hijo —un style reactivo dentro de su JSX—, mutarlo desde fuera desata una guerra: el binding revierte tu cambio en la siguiente actualización. Muta solo propiedades que el hijo no gobierna, o expón la intención por context y deja que el hijo la aplique. La manipulación directa es para decorar lo que nadie más toca.
En React, transformar hijos significa mapear descriptores y devolver nuevos descriptores: el DOM real lo materializa el reconciliador después. En Solid no hay ese intermediario, así que “manipular hijos” es, literalmente, tocar el DOM montado. Esto es a la vez una limitación y un superpoder. La limitación: no puedes “reconfigurar” un hijo cambiándole props como con cloneElement, porque el nodo ya nació con sus props resueltas; lo que llega a tus manos es materia, no un plano. El privilegio: puedes hacer cosas que en React exigen refs y efectos torpes —medir, decorar, reordenar nodos— con acceso directo y sincronía perfecta con la reactividad. La disciplina que separa al experto: envolver toda mutación en un efecto (para respetar el ciclo de vida y la limpieza), filtrar por tipo antes de tocar el DOM, y preferir la reactividad declarativa cuando la haya. Mutar nodos resueltos no es “hackear Solid”: es usar, con cuidado, la puerta que Solid deja abierta precisamente porque sus hijos son reales.
- Crea un
Menuque envuelva cada hijo en un<li role="menuitem">usando<For>sobretoArray(). - Añade un filtro que descarte los hijos que no sean
HTMLElementy cuenta cuántos quedaron. - Implementa
ListaColor: pinta cada elemento conprops.colordesde uncreateEffecty comprueba que reacciona al cambiar el color. - Añade o quita hijos dinámicamente (con un signal y
<For>en el padre) y verifica que el efecto vuelve a decorar solo los nuevos. - Mide un hijo con
getBoundingClientRect()desde uncreateEffecty usa su tamaño para posicionar un adorno; observa que el dato solo existe porque tienes el nodo real, no un plano.