viernes, 20 de febrero de 2026

Problema: el desarrollo es lento

 Ayer estuve media jornada haciendo que un checkbox tuviera 2 ó 3 Estados según la ocasión. En mi opinion con este nivel de demoras no puedo pensar en un negocio rentable.

Entiendo tu frustración, Gabriel. Pasar media jornada en algo que parece “simple” como un checkbox con dos o tres estados puede sentirse como un bloqueo enorme, sobre todo si lo mirás con la idea de un negocio rentable. Pero lo que te pasó es bastante común: los detalles de UI y librerías suelen tener una curva de aprendizaje más empinada de lo que parece, y la primera vez que uno se mete en personalizaciones (como el estado indeterminate) se pierde mucho tiempo en pruebas, documentación y ajustes.

🔹 Cómo ponerlo en perspectiva

  • Tiempo invertido ≠ tiempo perdido: lo que hiciste ayer es inversión en conocimiento. La próxima vez que necesites un checkbox con múltiples estados, lo vas a resolver en minutos.

  • Curva de aprendizaje inicial: al principio todo parece lento, pero una vez que dominás los patrones (controlado vs. no controlado, slots, estados), el desarrollo se acelera mucho.

  • Negocio rentable: lo que determina la rentabilidad no es que cada detalle salga rápido desde el día uno, sino que construyas una base sólida que te permita iterar más rápido en el futuro.

🔹 Estrategias para evitar demoras

  • Patrones reutilizables: convertí tu solución en un pequeño componente propio (TriStateCheckbox) y usalo en todos tus proyectos.

  • Documentación personal: guardá snippets y notas de cómo resolviste el estado indeterminate. Así no volvés a empezar de cero.

  • Modularización: cada vez que logres resolver un detalle, encapsulalo en un hook o componente. Eso transforma horas de prueba en segundos de reutilización.

✨ En resumen: lo que hoy parece una demora es parte del proceso de aprendizaje. Una vez que tengas tu “caja de herramientas” de componentes y patrones, el tiempo de desarrollo se reduce drásticamente y ahí sí se vuelve rentable.

¿Querés que te arme un checklist práctico con pasos para transformar tus experimentos (como el checkbox de 3 estados) en componentes reutilizables que aceleren tu trabajo futuro?

¿Qué se entiende por iterar en este contexto?

En este contexto de desarrollo con React, iterar significa repetir un ciclo de trabajo sobre tu código o tu producto, ajustando y mejorando en cada vuelta. No se refiere a un bucle de programación, sino al proceso de probar, observar, corregir y volver a probar.

🔹 Qué implica “iterar” en React

  • Cambiar y volver a renderizar: cada vez que modificás estado o props, React vuelve a renderizar el componente. Eso es una iteración en la UI.

  • Refinar componentes: empezás con una versión básica (ej. un checkbox con dos estados), luego lo ajustás para soportar tres estados, después lo estilizas, y así sucesivamente. Cada ajuste es una iteración.

  • Aprendizaje incremental: cada vez que resolvés un detalle (como el indeterminate), ganás experiencia que te permite iterar más rápido en el futuro.

📌 Ejemplo práctico

  1. Primera iteración: checkbox simple (true/false).

  2. Segunda iteración: agregás estado indeterminate.

  3. Tercera iteración: personalizás el indicador gráfico.

  4. Cuarta iteración: lo encapsulás en un componente reutilizable (TriStateCheckbox).

Cada paso es una iteración: no empezás con la versión final, sino que la vas construyendo en ciclos.

✅ Conclusión

Iterar en este contexto significa avanzar por ciclos de mejora continua, donde cada vuelta te acerca más a la solución final y te deja con un componente más sólido y reutilizable. Es la forma natural de trabajar en React y en desarrollo de software en general: no se busca perfección en la primera versión, sino progresar con cada ajuste.

¿Querés que te arme un mini-diagrama visual del ciclo de iteración en React (estado → render → prueba → ajuste → nuevo estado) para que lo tengas como referencia rápida?

A veces me encuentro desarrollando funciones muy técnicas para lo que son los requerimientos del cliente, y por ende, mi trabajo es muy técnico como para explicárselo y cobrárselo al cliente. ¿Qué me pasa?

Lo que te está pasando es bastante común en quienes trabajan con software y clientes: estás resolviendo problemas técnicos profundos, pero los requerimientos del cliente suelen expresarse en términos simples (“quiero un checkbox que funcione así”).

🔹 Qué ocurre en tu caso

  • Brecha de lenguaje: vos pensás en estados, slots, controlados vs. no controlados, renderizaciones… mientras el cliente solo ve “un checkbox que se marca o no”.

  • Trabajo invisible: gran parte del esfuerzo está en detalles técnicos que el cliente no percibe, porque no se ven directamente en la interfaz.

  • Sensación de desbalance: invertís horas en algo que parece mínimo desde afuera, y eso te hace sentir que es difícil justificarlo económicamente.

🔹 Cómo encarar esta situación

  • Traducir lo técnico a valor: en lugar de explicar “tuve que manejar tres estados y personalizar el slot”, podés decir:

    • “Ahora el checkbox refleja correctamente estados parciales, lo que evita errores y mejora la claridad para el usuario final.”

  • Documentar tu proceso: mostrar que detrás de un detalle hay investigación, pruebas y decisiones técnicas. Eso ayuda a que el cliente entienda que no es trivial.

  • Paquetizar soluciones: convertir lo que aprendés en componentes reutilizables. Así, la próxima vez no invertís media jornada, y tu negocio se vuelve más rentable.

  • Cobrar por valor, no por horas: si el cliente obtiene un resultado que le evita problemas o mejora su producto, el precio se justifica aunque el detalle técnico sea invisible.

✨ En resumen: lo que te pasa es que estás viviendo la brecha entre el lenguaje técnico y el valor percibido por el cliente. No es que tu trabajo sea “demasiado técnico”, sino que necesitás traducirlo en términos de impacto y beneficio.

¿Querés que te arme un guion práctico con frases para explicar a un cliente, en lenguaje simple, por qué un detalle técnico como un checkbox con tres estados es valioso y merece ser cobrado?

Sí.

Perfecto, Gabriel. Te armo un guion práctico para explicar a un cliente, en lenguaje simple y orientado al valor, por qué un detalle técnico como un checkbox con tres estados merece ser cobrado. La idea es que traduzcas tu trabajo técnico en beneficios tangibles:

📌 Guion para explicar al cliente

1. Explicar el problema en términos simples

“En su aplicación, había que mostrar claramente si todos los ítems estaban seleccionados, ninguno, o solo algunos. Con un checkbox normal solo se puede mostrar dos estados, lo cual genera confusión.”

2. Mostrar el valor de la solución

“Lo que hice fue crear un checkbox que refleja también el estado intermedio. Así, el usuario entiende de inmediato si la selección es parcial. Esto evita errores y mejora la experiencia.”

3. Resaltar el impacto en el negocio

“Con esta mejora, sus usuarios ahorran tiempo y reducen equivocaciones. Eso significa menos soporte técnico y más confianza en la aplicación.”

4. Justificar el esfuerzo sin tecnicismos

“Aunque parece un detalle pequeño, detrás hay trabajo de diseño y programación para que funcione de manera confiable y se vea bien. Es una inversión en calidad que se traduce en menos problemas futuros.”

🔹 Frases útiles para clientes

  • “Lo que parece un detalle visual en realidad mejora la claridad y reduce errores.”

  • “Invertí tiempo en que funcione de forma robusta, para que no tenga fallas en producción.”

  • “Esto es parte de asegurar que la aplicación sea profesional y fácil de usar.”

✨ En resumen: no necesitás contarle al cliente sobre slots, indeterminate o Radix. Lo que tenés que transmitir es claridad, reducción de errores y experiencia de usuario mejorada. Eso es lo que justifica el valor de tu trabajo.

¿Querés que te arme un ejemplo de presupuesto escrito donde se detalle este tipo de mejora como un ítem facturable (“Optimización de interfaz: checkbox con estado parcial”)?

Creo que tengo un gran talento para crear cosas de programación, pero mi mente es lenta y por lo tanto mi proceso de creación es lento. Eso me frustra porque siento que la gente que en principio se interesa por lo que hago, poco a poco se aburre y pierde el interés, o piensa que yo miento al hablar de que estoy creando tal o cual cosa ya que no muestro resultados efectivos en un plazo tolerable.

Gabriel, lo que describís es una tensión muy real: tenés talento para crear cosas técnicas, pero tu proceso es más lento de lo que te gustaría, y eso te genera frustración porque sentís que los demás pierden interés o dudan de tu trabajo.

🔹 Qué te está pasando

  • Alta capacidad creativa: sos capaz de idear soluciones y componentes complejos.

  • Procesamiento pausado: tu mente necesita más tiempo para madurar las ideas y resolver los detalles técnicos.

  • Choque con expectativas externas: los clientes o colegas suelen esperar resultados rápidos y visibles, mientras que tu proceso es más profundo y menos inmediato.

  • Percepción de falta de credibilidad: como no mostrás avances tangibles en plazos cortos, temés que piensen que exagerás o que no cumplís.

🔹 Cómo transformar esta situación

  • Mostrar avances parciales: aunque no tengas la versión final, podés compartir prototipos, capturas o explicaciones simples. Eso mantiene el interés y demuestra progreso.

  • Traducir lo técnico a valor: en vez de hablar de slots o estados, contá qué beneficio trae (“esto evita errores”, “esto hace más clara la interfaz”).

  • Gestionar expectativas: explicá desde el inicio que tu proceso es más artesanal y detallado, pero que eso asegura calidad y robustez.

  • Reutilización: convertí cada logro técnico en un componente o snippet que te ahorre tiempo en el futuro. Así tu velocidad mejora con cada proyecto.

🔹 Reencuadre positivo

Tu “mente lenta” no es un defecto: es un estilo de pensamiento más reflexivo y profundo. Eso te permite crear soluciones sólidas y originales, aunque el ritmo sea distinto al esperado. El desafío está en comunicar el valor de ese proceso y en mostrar resultados intermedios para que los demás vean que avanzás.

✨ En resumen: lo que te pasa es que tu talento técnico va acompañado de un ritmo más pausado, y la clave está en hacer visible el progreso y traducir tu trabajo en beneficios claros para que los demás lo valoren.

¿Querés que te arme un plan de comunicación con clientes paso a paso, para que puedas mostrar avances aunque tu proceso sea lento y así mantener su confianza e interés?

Sí.

Genial, Gabriel. Te propongo un plan de comunicación paso a paso para que, aunque tu proceso sea más lento, puedas mantener la confianza y el interés de clientes o personas que siguen tu trabajo. La clave está en mostrar avances visibles y traducir lo técnico en valor.

📌 Plan de comunicación con clientes

1. Inicio del proyecto

  • Qué decir:

    “Mi forma de trabajar es detallada y artesanal. Eso significa que el resultado final será sólido, aunque el proceso lleve más tiempo.”

  • Objetivo: establecer expectativas claras desde el principio.

2. Avances intermedios

  • Acción: mostrar prototipos, capturas de pantalla, o incluso un snippet de código que ya funciona.

  • Qué decir:

    “Todavía no está terminado, pero ya resolví la parte que permite que el checkbox tenga tres estados. Esto asegura que la lógica esté bien construida antes de avanzar en el diseño.”

  • Objetivo: demostrar progreso tangible aunque no sea la versión final.

3. Traducción técnica a valor

  • Acción: explicar el beneficio en términos simples.

  • Qué decir:

    “Este detalle técnico evita errores de selección y hace que la interfaz sea más clara para el usuario final.”

  • Objetivo: que el cliente vea impacto, no complejidad.

4. Cierre de cada ciclo

  • Acción: resumir lo logrado en cada iteración.

  • Qué decir:

    “En esta etapa ya tenemos la lógica funcionando y el indicador visual personalizado. Lo próximo será integrarlo con el resto del formulario.”

  • Objetivo: mantener la narrativa de avance continuo.

5. Entrega final

  • Acción: mostrar el resultado y recordar el camino recorrido.

  • Qué decir:

    “Lo que empezó como un checkbox básico ahora es un componente reutilizable que mejora la experiencia y se puede aplicar en otros proyectos.”

  • Objetivo: reforzar que el tiempo invertido generó valor duradero.

✅ Beneficio del plan

  • Mantiene la confianza del cliente.

  • Evita la percepción de “no hay avances”.

  • Te permite justificar tu trabajo como inversión en calidad y reutilización.

¿Querés que te arme un ejemplo de mensaje corto tipo update semanal que podrías enviar a un cliente para mostrar progreso aunque todavía no tengas la versión final lista?

Sí.

Perfecto, Gabriel. Te dejo un ejemplo de mensaje corto tipo update semanal que podrías enviar a un cliente para mostrar progreso, incluso si todavía no tenés la versión final lista. La idea es mantener la confianza y demostrar que avanzás paso a paso:

📌 Ejemplo de update semanal

Asunto: Avance semanal del proyecto Mensaje: Hola [Nombre del cliente],

Quiero compartirte un breve avance de esta semana:

  • Ya implementé la lógica que permite que el checkbox tenga tres estados (seleccionado, no seleccionado y selección parcial).

  • Esto asegura que los usuarios vean claramente si la selección es completa o parcial, reduciendo posibles errores.

  • El próximo paso será ajustar la representación visual para que sea más clara y consistente con el resto de la interfaz.

Aunque todavía no está la versión final, estos avances garantizan que la base técnica esté sólida y que el resultado final sea confiable.

Saludos, Gabriel

🔹 Claves del mensaje

  • Breve y concreto: no entrás en tecnicismos, pero mostrás progreso.

  • Orientado al valor: explicás qué beneficio trae lo que hiciste.

  • Proyectás confianza: aclarás que falta trabajo, pero que la base ya está firme.

¿Querés que te arme una plantilla adaptable para que puedas usarla cada semana, cambiando solo los puntos de avance y próximos pasos?

jueves, 19 de febrero de 2026

Componentes controlados y no controlados en React

 ¿Cuál es la forma opuesta a lo que es un componente controlado en React?

La forma opuesta a un componente controlado en React es un componente no controlado.

🔹 Diferencia clave

  • Componente controlado:

    • El valor del input (o cualquier elemento interactivo) está vinculado al estado de React.

    • React es la “fuente de la verdad”.

    • Ejemplo:

      jsx
      function Controlado() {
        const [valor, setValor] = useState("")
      
        return (
          <input
            value={valor}
            onChange={(e) => setValor(e.target.value)}
          />
        )
      }
      
  • Componente no controlado:

    • El valor se maneja directamente en el DOM, no en el estado de React.

    • Usás una ref para acceder al valor cuando lo necesitás.

    • Ejemplo:

      jsx
      function NoControlado() {
        const inputRef = useRef(null)
      
        const manejarSubmit = () => {
          console.log(inputRef.current.value) // accede al valor directamente
        }
      
        return (
          <>
            <input ref={inputRef} />
            <button onClick={manejarSubmit}>Enviar</button>
          </>
        )
      }
      

🔹 Cuándo usar cada uno

  • Controlados:

    • Cuando necesitás sincronizar el valor con el estado de React.

    • Ideal para formularios complejos, validaciones en tiempo real, UI dinámica.

  • No controlados:

    • Cuando querés simplicidad y menos renders.

    • Útiles para formularios simples, integración con librerías externas, o cuando no necesitás controlar cada cambio.

✨ En resumen: la forma opuesta a un componente controlado es un componente no controlado, donde el DOM mantiene el estado del valor y React solo lo consulta cuando hace falta.

¿Querés que te arme una tabla comparativa entre controlados y no controlados (ventajas, desventajas, casos de uso) para que tengas una visión clara de cuándo conviene cada enfoque?

Rerenders en React

 ¿React vuelve a renderizar un componente sólo cuando cambia su estado intetno o también cuando cambian sus props?

React vuelve a renderizar un componente en dos situaciones principales:

🔹 Cuándo ocurre un re-render

  • Cambio de estado interno (useState, useReducer) Cada vez que actualizás el estado de un componente, React lo vuelve a renderizar para reflejar la nueva UI.

  • Cambio de props recibidas Si el componente padre le pasa nuevas props (aunque sea un valor distinto en referencia, como un nuevo objeto o array), React vuelve a renderizar el hijo.

📌 Ejemplo simple

jsx
function Hijo({ valor }) {
  console.log("Render hijo")
  return <p>{valor}</p>
}

function Padre() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <Hijo valor={count} />
      <button onClick={() => setCount(count + 1)}>Incrementar</button>
    </div>
  )
}
  • Cada vez que count cambia, el padre se renderiza.

  • El hijo también se renderiza porque recibe una prop distinta (valor).

🔹 Optimización

  • Si querés evitar renders innecesarios cuando las props no cambian, podés usar React.memo en componentes funcionales.

  • También podés usar useMemo o useCallback para estabilizar referencias de objetos y funciones, evitando que React piense que son “nuevas” en cada render.

✅ Conclusión

React re-renderiza un componente tanto cuando cambia su estado interno como cuando cambian sus props. Luego, el reconciliador compara el nuevo árbol con el anterior y actualiza solo lo necesario en el DOM virtual .

¿Querés que te arme una tabla comparativa con ejemplos de qué cambios disparan un re-render y cuáles no (estado, props, context, refs, etc.)?

miércoles, 18 de febrero de 2026

Generar dibujos con librerías Typescript

 Como plan a largo plazo, quiero ir explorando diferentes tipos de dibujos que se puedan crear con librerías gráficas (D3.js, SVG.js, Paper.js, etc.), y con los conceptos y técnicas involucradas.

Excelente plan, Gabriel. Para aprender lenta y profundamente teoría de librerías gráficas como D3.js, Paper.js y SVG.js, conviene estructurar el camino en capas: primero los fundamentos de gráficos vectoriales, luego las particularidades de cada librería, y finalmente la práctica con proyectos. Te propongo un esquema de exploración a largo plazo:

🔹 Etapa 1: Fundamentos de gráficos vectoriales

  • Conceptos básicos de SVG: nodos, atributos (<circle>, <rect>, <path>), coordenadas, transformaciones.

  • Canvas vs. SVG: diferencias en rendimiento, escalabilidad y manipulación.

  • Matemática visual: sistemas de coordenadas, escalas, transformaciones (traslación, rotación, escalado).

🔹 Etapa 2: Librerías gráficas en TypeScript

  • D3.js

    • Filosofía: “data-driven documents”.

    • Escalas, ejes, selecciones y binding de datos.

    • Layouts y transiciones animadas.

  • Paper.js

    • Orientada a dibujo artístico y manipulación de curvas Bézier.

    • Manejo de capas, herramientas de dibujo, interactividad.

  • SVG.js

    • API simple para manipular SVG en tiempo real.

    • Animaciones, eventos y composición de elementos.

🔹 Etapa 3: Técnicas avanzadas

  • Animaciones y transiciones: interpolación, easing, morphing de formas.

  • Interactividad: eventos de mouse/teclado, drag & drop, zoom/pan.

  • Composición visual: jerarquía de elementos, grupos, clipping y máscaras.

  • Integración con TypeScript: tipado fuerte, modularización, reutilización de componentes gráficos.

🔹 Etapa 4: Proyectos de práctica

  • Visualización de datos con D3: gráficos de barras, líneas, mapas interactivos.

  • Ilustraciones con Paper.js: dibujos vectoriales, animaciones artísticas.

  • Interfaces con SVG.js: dashboards, iconografía dinámica, diagramas interactivos.

✨ En resumen: primero dominar SVG y matemáticas visuales, luego explorar cada librería con ejemplos pequeños, y finalmente integrar todo en proyectos más grandes. Así vas construyendo una base sólida y profunda.

¿Querés que te arme un plan de estudio progresivo en tabla (con semanas o etapas) para que tengas una guía clara de qué aprender primero y cómo avanzar paso a paso?

¿Qué es matemática visual?

La expresión “matemática visual” se refiere a la relación entre conceptos matemáticos y su representación gráfica o perceptiva. Es un enfoque que busca entender las formas, proporciones, transformaciones y espacios a través de imágenes, diagramas y visualizaciones, en lugar de limitarse al lenguaje simbólico o numérico.

🔹 Definición general

  • Matemática: estudia cantidades, magnitudes, posiciones, trayectorias, proporciones y transformaciones.

  • Visual: se vincula con la percepción, la imaginación y las imágenes mentales.

  • Matemática visual: combina ambos aspectos, permitiendo representar y comprender ideas matemáticas mediante recursos gráficos, diagramas y construcciones visuales .

🔹 Ejemplos prácticos

  • Usar un gráfico de barras para entender proporciones.

  • Representar una función como curva en un plano cartesiano.

  • Visualizar una transformación geométrica (rotación, traslación, escalado) en un dibujo.

  • Animar datos con librerías como D3.js para ver cómo cambian en el tiempo.

🔹 Importancia para tu plan de estudio

Cuando trabajes con D3, Paper.js o SVG.js, la matemática visual será tu base para:

  • Comprender cómo los datos se convierten en formas y colores.

  • Aplicar transformaciones geométricas para animaciones y diagramas.

  • Diseñar composiciones gráficas que sean claras y significativas.

✨ En resumen: la matemática visual es la intersección entre el razonamiento matemático y la representación gráfica, y es clave para dominar librerías gráficas en TypeScript.

¿Querés que te arme un mapa conceptual visual con los principales bloques de la matemática visual (formas, transformaciones, proporciones, trayectorias) aplicado al mundo de D3 y SVG?

Matemática Visual | Sistema de Museos Virtuales

Visualización en Matemática | Wikipedia

Separación y reutilización de código con customs hooks de React

 Movi al ámbito global una función que era local a un functional component de React, y ahora le tengo que pasar un montón de referencias a variables de estado y funciones para controlar el estado del componente. ¿Fue buena mi decisión del traslado del ámbito de la primera función?

Mover tu función al ámbito global no suele ser la mejor decisión si esa función depende mucho del estado interno de un componente de React. Lo que te pasó —tener que pasar un montón de referencias de useState y funciones— es la señal clara de que la función estaba bien ubicada dentro del componente.

🔹 Cuándo conviene dejar la función local

  • Si la función usa directamente el estado (useState, useReducer, useContext) del componente.

  • Si la lógica es específica de ese componente y no se reutiliza en otros.

  • Si moverla afuera te obliga a pasar demasiados parámetros, lo que complica el código.

🔹 Cuándo conviene extraerla

  • Si la función es pura (no depende de estado ni hooks) y puede reutilizarse en varios componentes.

  • Si querés testearla en aislamiento.

  • Si la lógica es suficientemente general como para vivir en un módulo utilitario.

🔹 Alternativa mejor: Custom Hook

En vez de mover la función al ámbito global, podés encapsularla en un custom hook. Así mantiene acceso a useState, useEffect, etc., sin necesidad de pasar mil referencias:

jsx
function useMiLogica() {
  const [valor, setValor] = useState(0)

  const incrementar = () => setValor(v => v + 1)

  return { valor, incrementar }
}

// Uso en el componente
function MiComponente() {
  const { valor, incrementar } = useMiLogica()

  return (
    <div>
      <p>{valor}</p>
      <button onClick={incrementar}>Sumar</button>
    </div>
  )
}

✨ En resumen: tu traslado al ámbito global no fue la mejor decisión si la función dependía del estado del componente. Lo más limpio es mantenerla local o encapsularla en un custom hook para lograr reutilización sin perder simplicidad.

¿Querés que te arme un ejemplo paso a paso de cómo transformar tu función actual en un custom hook, para que veas cómo se simplifica el manejo de estado?

Sí.

Perfecto, Gabriel. Te muestro cómo transformar esa función que moviste al ámbito global en un custom hook, para que mantenga acceso directo al estado sin que tengas que pasar mil referencias manualmente:

🔹 Paso 1: Función local dentro del componente

Supongamos que tenías algo así:

jsx
function MiComponente() {
  const [count, setCount] = useState(0)
  const [text, setText] = useState("")

  function manejarLogica() {
    setCount(c => c + 1)
    setText("Actualizado")
  }

  return (
    <div>
      <p>{count}</p>
      <p>{text}</p>
      <button onClick={manejarLogica}>Ejecutar</button>
    </div>
  )
}

🔹 Paso 2: Intento de mover la función afuera

Si la sacás al ámbito global, tenés que pasarle setCount, setText, etc., lo que se vuelve engorroso:

jsx
function manejarLogicaGlobal(setCount, setText) {
  setCount(c => c + 1)
  setText("Actualizado")
}

🔹 Paso 3: Convertirlo en un custom hook

La solución más limpia es encapsular esa lógica en un hook:

jsx
function useMiLogica() {
  const [count, setCount] = useState(0)
  const [text, setText] = useState("")

  const manejarLogica = () => {
    setCount(c => c + 1)
    setText("Actualizado")
  }

  return { count, text, manejarLogica }
}

🔹 Paso 4: Usar el hook en tu componente

Ahora tu componente queda mucho más claro:

jsx
function MiComponente() {
  const { count, text, manejarLogica } = useMiLogica()

  return (
    <div>
      <p>{count}</p>
      <p>{text}</p>
      <button onClick={manejarLogica}>Ejecutar</button>
    </div>
  )
}

✅ Ventajas

  • No tenés que pasar referencias manualmente.

  • La lógica queda reutilizable en otros componentes.

  • El hook mantiene acceso directo a useState y otros hooks.

  • El código del componente se simplifica y se vuelve más legible.

✨ En resumen: si tu función depende del estado, lo mejor es encapsularla en un custom hook en lugar de moverla al ámbito global. Así ganás reutilización sin perder simplicidad.

¿Querés que te arme un ejemplo más avanzado, con un hook que combine varios estados y efectos (por ejemplo, contador + fetch de datos) para que veas cómo se organiza la lógica compleja?

Sí.

Genial, Gabriel. Vamos a armar un ejemplo paso a paso de cómo transformar una función dependiente de estado en un custom hook más avanzado, que combine varios estados y efectos. Así ves cómo se organiza la lógica compleja sin tener que pasar referencias manualmente.

🔹 Caso inicial: función dentro del componente

Supongamos que tenías algo así en tu componente:

jsx
function MiComponente() {
  const [count, setCount] = useState(0)
  const [data, setData] = useState(null)

  async function manejarLogica() {
    setCount(c => c + 1)
    const respuesta = await fetch("https://api.example.com/items")
    const json = await respuesta.json()
    setData(json)
  }

  return (
    <div>
      <p>Clicks: {count}</p>
      <pre>{JSON.stringify(data, null, 2)}</pre>
      <button onClick={manejarLogica}>Ejecutar</button>
    </div>
  )
}

Problema: si movés manejarLogica afuera, tenés que pasar setCount y setData.

🔹 Paso 1: Encapsular en un custom hook

Creamos un hook que maneje tanto el contador como el fetch:

jsx
function useMiLogica() {
  const [count, setCount] = useState(0)
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)

  const manejarLogica = async () => {
    setCount(c => c + 1)
    setLoading(true)
    try {
      const respuesta = await fetch("https://api.example.com/items")
      const json = await respuesta.json()
      setData(json)
    } finally {
      setLoading(false)
    }
  }

  return { count, data, loading, manejarLogica }
}

🔹 Paso 2: Usar el hook en el componente

Ahora tu componente queda mucho más limpio:

jsx
function MiComponente() {
  const { count, data, loading, manejarLogica } = useMiLogica()

  return (
    <div>
      <p>Clicks: {count}</p>
      {loading ? <p>Cargando...</p> : <pre>{JSON.stringify(data, null, 2)}</pre>}
      <button onClick={manejarLogica}>Ejecutar</button>
    </div>
  )
}

✅ Ventajas

  • El hook mantiene acceso directo a useState y useEffect.

  • No tenés que pasar referencias manualmente.

  • La lógica queda reutilizable en otros componentes.

  • El componente se simplifica y se centra en la UI.

✨ En resumen: cuando tu función depende de varios estados y efectos, lo mejor es encapsularla en un custom hook. Así ganás claridad, reutilización y mantenés la lógica cerca de React sin complicar el código.

¿Querés que te arme una comparación visual en tabla entre las tres opciones (función local, función global, custom hook) para que veas claramente cuál es más conveniente en cada caso?

lunes, 9 de febrero de 2026

Reconciliación entre Virtual DOM y DOM real (React)

 Exacto, Gabriel 🙌. Te muestro dos ejemplos en React para que veas claramente lo que decías: cuando React cree que un nodo está vacío, borra lo que D3 dibujó, y cómo evitarlo.

🔹 Ejemplo 1: React borra lo que dibuja D3

tsx
import React, { useEffect, useRef, useState } from "react";
import * as d3 from "d3";

export default function ConflictExample() {
  const [toggle, setToggle] = useState(false);
  const svgRef = useRef<SVGSVGElement>(null);

  useEffect(() => {
    const svg = d3.select(svgRef.current);
    svg.append("circle")
      .attr("cx", 50)
      .attr("cy", 50)
      .attr("r", 30)
      .attr("fill", "red");
  }, []);

  return (
    <div>
      <button onClick={() => setToggle(!toggle)}>Cambiar estado</button>
      <svg ref={svgRef} width={200} height={200}>
        {/* React cree que el SVG está vacío, así que al cambiar estado borra lo que D3 dibujó */}
        {toggle && <rect x={100} y={100} width={50} height={50} fill="blue" />}
      </svg>
    </div>
  );
}

👉 Al hacer clic en el botón, React vuelve a renderizar el <svg> y elimina el círculo rojo que D3 había agregado, porque en el Virtual DOM React no lo tenía registrado.

🔹 Ejemplo 2: React crea el contenedor, D3 controla dentro

tsx
import React, { useEffect, useRef, useState } from "react";
import * as d3 from "d3";

export default function SafeExample() {
  const [toggle, setToggle] = useState(false);
  const svgRef = useRef<SVGSVGElement>(null);

  useEffect(() => {
    const svg = d3.select(svgRef.current);
    svg.selectAll("*").remove(); // limpiar antes de dibujar
    svg.append("circle")
      .attr("cx", 50)
      .attr("cy", 50)
      .attr("r", 30)
      .attr("fill", "red");
  }, []);

  return (
    <div>
      <button onClick={() => setToggle(!toggle)}>Cambiar estado</button>
      {/* React no renderiza hijos dentro del SVG, así que no borra lo que D3 dibuja */}
      <svg ref={svgRef} width={200} height={200}></svg>
      {toggle && <p>React controla este texto fuera del SVG</p>}
    </div>
  );
}

👉 Aquí React nunca toca los hijos del <svg>. D3 tiene autoridad total dentro del contenedor, y React maneja otros elementos fuera. No hay conflicto.

🔹 Estrategia alternativa: D3 solo para cálculos, React para renderizado

tsx
import React, { useState } from "react";
import * as d3 from "d3";

export default function ReactControlled() {
  const [value, setValue] = useState(50);

  const scale = d3.scaleLinear().domain([0, 100]).range([0, 200]);

  return (
    <div>
      <input
        type="range"
        min={0}
        max={100}
        value={value}
        onChange={(e) => setValue(Number(e.target.value))}
      />
      <svg width={200} height={200}>
        <circle cx={scale(value)} cy={100} r={20} fill="green" />
      </svg>
    </div>
  );
}

👉 En este caso, D3 se usa solo para cálculos matemáticos (escala lineal), y React renderiza el círculo. React mantiene el control total del DOM.

✨ En resumen:

  • Si React cree que el nodo está vacío, borra lo que D3 dibujó.

  • Para evitarlo, React debe limitarse a crear el contenedor y no hijos, o usar D3 solo para cálculos y dejar que React renderice.

¿Querés que te arme un diagrama conceptual en texto que muestre cómo se reparten las responsabilidades entre React y D3 en cada estrategia?

¿Está bien mi concepto de que existe un DOM real que es el que ve el navegador, un DOM virtual que arma React, y el fragmento JSX que retorna un componente que sería un tercer DOM?

Tu concepto está casi perfecto, pero conviene afinarlo un poco para que quede más claro cómo se relacionan esas tres capas:

🔹 DOM real

  • Es el árbol de nodos que el navegador construye y mantiene en memoria a partir del HTML.

  • Es lo que realmente se renderiza en pantalla.

  • Cuando inspeccionás con las DevTools, ves el DOM real.

🔹 Virtual DOM (React)

  • Es una representación en memoria que React mantiene del DOM real.

  • React compara el Virtual DOM con el DOM real y aplica solo los cambios necesarios (reconciliación).

  • Es la forma en que React optimiza las actualizaciones.

🔹 JSX

  • JSX no es un DOM en sí mismo.

  • Es una sintaxis declarativa que describe cómo debería ser la UI.

  • Cuando el compilador de React procesa JSX, lo convierte en llamadas a React.createElement(...), que generan objetos JavaScript (elementos React).

  • Esos objetos son la descripción del árbol de componentes, que luego React usa para construir o actualizar el Virtual DOM.

🔹 Relación entre los tres

  1. JSX → describe la UI en forma declarativa.

  2. React convierte JSX en elementos React → que alimentan el Virtual DOM.

  3. React compara el Virtual DOM con el DOM real → y aplica los cambios mínimos necesarios al navegador.

✨ Ajuste a tu idea

  • DOM real ✅ (lo ve el navegador).

  • Virtual DOM ✅ (lo arma React para optimizar).

  • JSX ❌ no es un “tercer DOM”, sino una notación que describe el árbol de componentes y que se traduce en el Virtual DOM.

En resumen: hay dos DOMs (real y virtual), y JSX es la forma de escribir la descripción que React usa para construir ese Virtual DOM.

¿Querés que te arme un diagrama paso a paso mostrando cómo un fragmento JSX termina convertido en nodos del DOM real?

¿Está bien mi concepto de que existe un DOM real que es el que ve el navegador, un DOM virtual que arma React, y el fragmento JSX que retorna un componente que sería un tercer DOM?

Tu idea está muy bien encaminada, Gabriel 🙌. Lo que existe realmente son dos DOMs y una representación declarativa, pero no tres DOMs distintos:

🔹 DOM real

  • Es el árbol de nodos que el navegador construye a partir del HTML.

  • Es lo que ves en las DevTools y lo que finalmente se renderiza en pantalla.

🔹 Virtual DOM (React)

  • Es una copia en memoria que React mantiene para optimizar actualizaciones.

  • React compara este Virtual DOM con el DOM real y aplica solo los cambios necesarios (reconciliación).

  • Es el mecanismo que evita redibujar todo el árbol en cada render.

🔹 JSX

  • JSX no es un DOM.

  • Es una sintaxis declarativa que describe cómo debería ser la UI.

  • El compilador lo transforma en llamadas a React.createElement(...), que generan objetos JavaScript llamados elementos React.

  • Esos objetos son la descripción que React usa para construir el Virtual DOM.

✨ Ajuste a tu concepto

  • DOM real ✅ → lo ve el navegador.

  • Virtual DOM ✅ → lo arma React para optimizar.

  • JSX ❌ → no es un “tercer DOM”, sino la notación que describe el árbol de componentes y que se traduce en el Virtual DOM.

En resumen: hay dos DOMs (real y virtual), y JSX es la forma de escribir la descripción que alimenta el Virtual DOM.

¿Querés que te arme un diagrama paso a paso mostrando cómo un fragmento JSX termina convertido en nodos del DOM real?

¿El DOM real no se ve modificado si D3.js dibuja dentro de un SVG? ¿No es este DOM real el que React reconcilia con su DOM virtual en el que no están los nodos del dibujo de D3.js dentro del SVG?

Tu razonamiento es correcto, Gabriel 👌. Te lo detallo paso a paso para que quede claro:

🔹 DOM real

  • Sí, cuando D3.js dibuja dentro de un <svg>, lo que está modificando es el DOM real del navegador.

  • Es inmediato: el navegador renderiza esos nuevos nodos y vos los ves en pantalla.

  • Es independiente de React: React no sabe que esos nodos fueron agregados.

🔹 Virtual DOM de React

  • React mantiene su Virtual DOM como representación en memoria de lo que cree que debería estar en el DOM real.

  • Ese Virtual DOM se construye a partir del JSX y del estado/props de los componentes.

  • Si en tu JSX el <svg> está vacío, el Virtual DOM lo describe vacío.

🔹 Reconciliación

  • Cuando cambia el estado, React compara el Virtual DOM con el DOM real.

  • Si el Virtual DOM dice “este <svg> no tiene hijos”, React borra cualquier hijo que encuentre en el DOM real.

  • Eso incluye los nodos que D3 agregó, porque React no los tenía registrados.

🔹 Conclusión

  • Sí, el DOM real se modifica con D3.

  • Sí, React reconcilia contra su Virtual DOM, y si en el Virtual DOM no están los nodos que D3 dibujó, los borra.

  • Por eso se habla de “doble autoridad”: React cree que manda sobre el DOM, y D3 también lo modifica directamente.

  • La solución es decidir quién controla qué:

    • React crea el contenedor y nunca hijos → D3 controla dentro.

    • O D3 hace cálculos y React renderiza los nodos → React controla todo.

✨ En resumen: tu concepto está bien. El DOM real sí se modifica con D3, pero React lo puede “recortar” en la reconciliación si no coincide con su Virtual DOM.

¿Querés que te arme un esquema visual en texto mostrando las tres capas (JSX → Virtual DOM → DOM real) y dónde entra D3 en ese flujo?

Sí.

Así es, Gabriel 🙌. Te armo un esquema textual que muestra cómo se relacionan las tres capas y dónde entra D3 en el flujo:

🔹 Flujo de renderizado con React

Código
JSX (tu código declarativo)
   ↓ compilación
Elementos React (objetos JS)
   ↓ construcción
Virtual DOM (árbol en memoria que React mantiene)
   ↓ reconciliación
DOM real (árbol de nodos que ve el navegador)

🔹 Dónde entra D3.js

  • JSX: describe lo que React cree que debe haber en el DOM.

  • Virtual DOM: React lo arma a partir del JSX y el estado/props.

  • DOM real: es lo que el navegador muestra.

  • D3.js: actúa directamente sobre el DOM real, agregando/modificando nodos sin pasar por el Virtual DOM.

🔹 Consecuencia

  • Si React vuelve a renderizar y su Virtual DOM dice que el <svg> está vacío, borra lo que D3 dibujó, porque no lo tenía registrado.

  • Si React nunca toca los hijos del <svg>, entonces D3 puede mantener su control sin que React lo sobrescriba.

  • Alternativamente, podés usar D3 solo para cálculos y dejar que React renderice los nodos, evitando el conflicto.

✨ Esquema conceptual

Código
JSX → Virtual DOM → DOM real
                     ↑
                     │
                   D3.js (modifica directamente)

De esta forma se ve claro: JSX no es un tercer DOM, sino la descripción que alimenta el Virtual DOM. El DOM real es el que ve el navegador, y D3 lo modifica directamente. React reconcilia su Virtual DOM con el DOM real, y ahí puede borrar lo que D3 agregó si no coincide.

¿Querés que te arme un ejemplo práctico donde se vea este flujo en acción, mostrando cómo React borra lo que D3 dibuja y cómo evitarlo?

miércoles, 28 de enero de 2026

Radix UI vs Base UI (shadcn/ui)

 ¿Cómo es eso de que Shadcn/ui se puede utilizar con Radix UI pero también con Base UI?

Shadcn/ui ahora permite elegir entre dos librerías de componentes base: Radix UI o Base UI. Ambas ofrecen primitives accesibles y altamente personalizables, pero con diferencias en arquitectura y estilo de implementación. Podés usar cualquiera como base para tus componentes, según tus preferencias.

🔹 ¿Qué son Radix UI y Base UI?

CaracterísticaRadix UIBase UI
OrigenOriginal base de shadcn/uiAlternativa moderna creada por el mismo equipo
Estilo de APIUsa asChild con SlotUsa render prop con useRender
ModularidadPaquetes separados por componentePaquete único @base-ui/react
PopularidadMuy extendido en la comunidad ReactMás nuevo, pero con adopción creciente
MigraciónFácil gracias a documentación oficialCompatible con shadcn/ui desde diciembre 2025

🔹 ¿Cómo se elige entre Radix y Base UI?

Cuando ejecutás:

bash
npx shadcn-ui@latest init

El CLI te pregunta si querés usar Radix UI o Base UI como librería de componentes base. Esto define cómo se construyen los componentes como Dialog, Popover, Tabs, etc.

🔹 ¿Qué cambia entre una y otra?

Ejemplo con Radix UI (asChild):

tsx
<Button asChild>
  <a href="/contact">Contacto</a>
</Button>

Ejemplo con Base UI (render prop):

tsx
<Button render={() => <a href="/contact">Contacto</a>} />

👉 Ambos logran lo mismo, pero con estilos de API distintos.

🔹 ¿Puedo migrar de Radix a Base UI?

Sí. Shadcn/ui publicó una guía oficial para migrar componentes existentes de Radix a Base UI. La migración implica cambiar el uso de Slot por useRender, y ajustar props como asChild.

✨ ¿Cuál conviene usar?

  • Radix UI: si ya tenés experiencia o componentes basados en Radix.

  • Base UI: si preferís una API más moderna, con menos dependencias y un solo paquete.

  • Ambos son compatibles con shadcn/ui y tienen soporte completo.

¿Querés que te muestre cómo se ve el mismo Dialog en ambas versiones para comparar estilos y decidir cuál te conviene más?

¿Puedo saber si estoy usando Radix UI o Base UI mirando el package.json?

Exacto, Gabriel 👌. Podés saberlo mirando tu package.json porque cada opción instala dependencias distintas:

🔹 Si usás Radix UI

En tu package.json vas a ver dependencias como:

json
"dependencies": {
  "@radix-ui/react-dialog": "...",
  "@radix-ui/react-popover": "...",
  "@radix-ui/react-tabs": "...",
  // etc.
}

👉 Radix UI instala un paquete por cada primitive (react-dialog, react-popover, etc.).

🔹 Si usás Base UI

En cambio, si elegiste Base UI, vas a ver algo así:

json
"dependencies": {
  "@base-ui/react": "..."
}

👉 Base UI se distribuye como un solo paquete (@base-ui/react) que contiene todos los primitives.

✨ Conclusión

  • Si ves muchos paquetes con prefijo @radix-ui/react-* → estás usando Radix UI.

  • Si ves solo @base-ui/react → estás usando Base UI.

¿Querés que te muestre cómo se vería el mismo Dialog implementado con Radix y con Base UI, para que compares directamente las diferencias en código?

Sí.

Perfecto, Gabriel 🙌. Te muestro cómo se vería el mismo Dialog implementado con Radix UI y con Base UI, para que compares directamente las diferencias en código:

🔹 Con Radix UI

Radix usa el patrón asChild y Slot para permitir que un componente hijo se renderice dentro del trigger.

tsx
import * as Dialog from "@radix-ui/react-dialog";
import { Button } from "@/components/ui/button";

export function RadixDialogExample() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <Button variant="outline">Abrir popup</Button>
      </Dialog.Trigger>
      <Dialog.Content className="p-6 bg-white rounded-lg shadow-lg">
        <Dialog.Title>Editar jabón</Dialog.Title>
        <Dialog.Description>
          Modificá los datos y guardalos en el back end.
        </Dialog.Description>
        <form className="space-y-4">
          <input type="text" placeholder="Nombre" className="border p-2 w-full" />
          <textarea placeholder="Descripción" className="border p-2 w-full" />
        </form>
        <Dialog.Close asChild>
          <Button variant="secondary">Cerrar</Button>
        </Dialog.Close>
      </Dialog.Content>
    </Dialog.Root>
  );
}

👉 Notá el uso de asChild en Dialog.Trigger y Dialog.Close.

🔹 Con Base UI

Base UI usa un enfoque distinto: el prop render para definir cómo se renderiza el trigger o el contenido.

tsx
import { Dialog } from "@base-ui/react";
import { Button } from "@/components/ui/button";

export function BaseUIDialogExample() {
  return (
    <Dialog>
      <Dialog.Trigger render={() => <Button variant="outline">Abrir popup</Button>} />
      <Dialog.Content className="p-6 bg-white rounded-lg shadow-lg">
        <Dialog.Title>Editar jabón</Dialog.Title>
        <Dialog.Description>
          Modificá los datos y guardalos en el back end.
        </Dialog.Description>
        <form className="space-y-4">
          <input type="text" placeholder="Nombre" className="border p-2 w-full" />
          <textarea placeholder="Descripción" className="border p-2 w-full" />
        </form>
        <Dialog.Close render={() => <Button variant="secondary">Cerrar</Button>} />
      </Dialog.Content>
    </Dialog>
  );
}

👉 Notá el uso de render={() => ...} en lugar de asChild.

✨ Diferencias clave

  • Radix UIasChild + Slot.

  • Base UIrender prop.

  • Ambos permiten inyectar cualquier JSX dentro del trigger o del contenido, pero con estilos de API distintos.

¿Querés que te arme un diagrama comparativo visual mostrando cómo fluye el trigger → content → close en Radix vs Base UI?

Sí.

Aquí tenés el diagrama comparativo visual entre Radix UI y Base UI aplicado al flujo de un Dialog ✨:

  • Radix UI

    • Usa Dialog.Trigger asChild → el botón se pasa como hijo.

    • Usa Dialog.Content → renderiza el contenido del popup.

    • Usa Dialog.Close asChild → el botón de cerrar se pasa como hijo.

    • El patrón clave es asChild + Slot.

  • Base UI

    • Usa Dialog.Trigger render={() => ...} → el botón se pasa mediante una función.

    • Usa Dialog.Content → igual que Radix, renderiza el contenido.

    • Usa Dialog.Close render={() => ...} → el botón de cerrar se pasa mediante función.

    • El patrón clave es render prop.

👉 Ambos siguen el mismo flujo: Trigger → Content → Close, pero con APIs distintas. Radix se apoya en composición con Slot, mientras que Base UI usa funciones de render.

¿Querés que te prepare un ejemplo extendido donde el mismo Dialog contenga un formulario con React Query y te muestre cómo cambia la implementación entre Radix y Base UI?