Configuración de Máquina de Estados: Crear y Gestionar Vistas y Pasos

Clase 24 de 31Curso de React Avanzado

Resumen

Definir una máquina de estados clara y tipada acelera el desarrollo de flujos tipo formulario o Wizard. Aquí verás cómo estructurar la configuración con su initial step, los steps con su blocker y las views que renderizan cada paso, además de cómo actualizar el estado con setState sin perder datos previos.

¿Cómo crear la configuración de la máquina de estados?

La base es un objeto de configuración que sigue una interfaz similar a StateMachineConfig. Incluye el paso inicial, los pasos con su lógica de avance y las vistas que se renderizan por clave. El flujo parte del paso 1, valida nombre en el paso 1, edad en el paso 2 y muestra una confirmación final.

  • Define el paso inicial con la clave del primer paso.
  • En steps, cada clave mapea a un blocker que devuelve booleano.
  • En views, cada clave mapea a una función que renderiza un div, input u otro componente y recibe state y setState.
// Tipos del estado y claves de pasos
 type StepKey = 'step1' | 'step2' | 'confirmation';
 type WizardState = { name: string; age?: number };

// Configuración principal
const stateMachineConfig: StateMachineConfig<WizardState, StepKey> = {
  initialStep: 'step1',
  steps: {
    step1: (state) => !!state.name,          // puede avanzar si hay nombre
    step2: (state) => !!state.age,           // puede avanzar si hay edad
    confirmation: () => true,                // siempre permite avanzar
  },
  views: {
    step1: (state, setState) => (
      <div>
        <input
          value={state.name}
          onChange={(e) =>
            setState((prev) => ({ ...prev, name: e.target.value }))
          }
          placeholder="full name"
        />
      </div>
    ),
    step2: (state, setState) => (
      <div>
        <input
          type="number"
          value={state.age ?? ''}
          onChange={(e) =>
            setState((prev) => ({
              ...prev,
              age: parseInt(e.target.value, 10),
            }))
          }
        />
      </div>
    ),
    confirmation: (state) => (
      <p>
        El nombre ingresado, {state.name}, es {state.age} yearsOld.
      </p>
    ),
  },
};

¿Qué validaciones y blockers controlan el avance?

Cada paso define su propia condición de avance. La idea es simple: si el blocker retorna true, el flujo continúa; si retorna false, permanece en la vista actual.

  • Paso 1: el blocker valida state.name. Se usa conversión a booleano para strings con !!state.name.
  • Paso 2: el blocker valida state.age. Tras capturar el valor, se convierte con parseInt.
  • Confirmación: retorna true porque el Wizard ya finaliza ahí.

Claves a retener: - key: identifica cada paso en steps y views. - blocker: función que decide el avance por paso. - views: funciones que reciben state y setState para renderizar y actualizar.

¿Cómo se tipa y se organiza para escalar?

El tipado garantiza que state, los nombres de los pasos y las views estén alineados. Además, al actualizar el estado, se debe preservar lo anterior para no perder datos capturados.

  • Usa un tipo de estado, por ejemplo WizardState, con name y age.
  • Pasa los nombres de pasos como unión de literales a StateMachineConfig.
  • En onChange, usa setState((prev) => ({ ...prev, campo })) para mantener prev intacto.
  • En el paso 2, convierte el valor con parseInt para mantener age numérico.

Buenas prácticas del flujo: - Renderiza elementos simples en views (div, input), o importa componentes externos. - Añade placeholder descriptivo como "full name" para mejorar la captura. - Evita borrar campos ya definidos utilizando el patrón de propagación ...prev.

¿Tienes dudas o un caso de uso diferente para tu Wizard? Cuéntame en los comentarios y lo revisamos juntos.