Skip to content

Instantly share code, notes, and snippets.

@warderer
Last active November 11, 2025 01:19
Show Gist options
  • Select an option

  • Save warderer/5f5f634d306bc5614e7f5108187eaea9 to your computer and use it in GitHub Desktop.

Select an option

Save warderer/5f5f634d306bc5614e7f5108187eaea9 to your computer and use it in GitHub Desktop.
[React Advanced Hooks Example Form] An example multistep form with useRef, useReducer and useCallback #devf #masterfrontend #introreact
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.form-container {
background-color: #ffffff;
padding: 2rem 3rem;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 450px;
min-height: 400px;
display: flex;
flex-direction: column;
}
.form-step {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
h2 {
color: #1c1e21;
text-align: center;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
display: flex;
flex-direction: column;
}
label {
margin-bottom: 0.5rem;
font-weight: 600;
color: #4b4f56;
}
input {
padding: 0.8rem;
border: 1px solid #dddfe2;
border-radius: 6px;
font-size: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
input:focus {
border-color: #1877f2;
box-shadow: 0 0 0 2px rgba(24, 119, 242, 0.2);
outline: none;
}
.form-group small {
color: #c0392b;
margin-top: 0.25rem;
}
.form-navigation {
display: flex;
justify-content: space-between;
margin-top: 2rem;
gap: 1rem;
}
.form-navigation button {
flex-grow: 1;
padding: 0.8rem 1rem;
font-size: 1rem;
font-weight: 600;
border-radius: 6px;
border: none;
cursor: pointer;
transition: background-color 0.2s;
background-color: #1877f2;
color: white;
}
.form-navigation button.secondary {
background-color: #e4e6eb;
color: #4b4f56;
}
.form-navigation button:disabled {
background-color: #a0b3d6;
cursor: not-allowed;
}
.form-navigation button:not(:disabled):hover {
filter: brightness(1.1);
}
.success {
text-align: center;
}
.summary {
background-color: #f0f2f5;
border-radius: 8px;
padding: 1rem;
margin: 1.5rem 0;
text-align: left;
}
.summary p {
margin: 0.5rem 0;
}
// TODO: Importar useRef de React
import { useEffect } from 'react'
function Step1({ formData, handleFieldChange, nextStep }) {
//TODO: Llamar a inputRef
//
useEffect(() => {
//TODO: Enfoca el primer campo de entrada (nameInputRef) cuando el componente se monta
}, [])
const canProceed = formData.name && formData.email
return (
<div className="form-step">
<h2>Paso 1: Información Personal</h2>
<div className="form-group">
<label htmlFor="name">Nombre</label>
<input
type="text"
id="name"
name="name"
ref={'ASIGNAMOS LA REFERENCIA AQUI'}
value={formData.name}
onChange={handleFieldChange}
placeholder="Ej: César Guerra"
/>
</div>
<div className="form-group">
<label htmlFor="email">Correo Electrónico</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleFieldChange}
placeholder="Ej: cesar.guerra@correo.com"
/>
</div>
<div className="form-navigation">
<button onClick={nextStep} disabled={!canProceed}>
Siguiente
</button>
</div>
</div>
);
}
export default Step1
// TODO: Importar useRef de React
import { useEffect } from 'react'
function Step2({ formData, handleFieldChange, prevStep, nextStep }) {
//TODO: Llamar a inputRef
useEffect(() => {
//TODO: Enfoca el primer campo de entrada (passwordInputRef) cuando el componente se monta
}, [])
const canProceed = formData.password.length >= 8;
return (
<div className="form-step">
<h2>Paso 2: Seguridad</h2>
<div className="form-group">
<label htmlFor="password">Contraseña</label>
<input
type="password"
id="password"
name="password"
ref={'ASIGNAMOS LA REFERENCIA AQUI'}
value={formData.password}
onChange={handleFieldChange}
placeholder="Mínimo 8 caracteres"
/>
{!canProceed && formData.password && (
<small>La contraseña debe tener al menos 8 caracteres.</small>
)}
</div>
<div className="form-navigation">
<button onClick={prevStep} className="secondary">
Anterior
</button>
<button onClick={nextStep} disabled={!canProceed}>
Finalizar Registro
</button>
</div>
</div>
);
}
export default Step2
function Success({ formData, reset }) {
return (
<div className="form-step success">
<h2>¡Registro Exitoso!</h2>
<p>Gracias por registrarte. Aquí está tu información:</p>
<div className="summary">
<p><strong>Nombre:</strong> {formData.name}</p>
<p><strong>Correo Electrónico:</strong> {formData.email}</p>
</div>
<div className="form-navigation">
<button onClick={reset}>Crear un nuevo registro</button>
</div>
</div>
);
}
export default Success
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment