Logo WingedBits

Published

- 4 min read

Buenas Prácticas con Hooks en React: Guía Definitiva

img of Buenas Prácticas con Hooks en React: Guía Definitiva

¡Hola! Hoy vamos a hablar de los hooks en React y cómo usarlos de la mejor manera posible. Si alguna vez te has preguntado por qué tus componentes se comportan de manera extraña o por qué tienes problemas con los efectos secundarios, este artículo es para ti.

Las Reglas de Oro de los Hooks

Antes de empezar, recordemos las dos reglas más importantes:

  1. Solo usa hooks en el nivel superior

       // ❌ Mal
    if (condicion) {
    	useEffect(() => {});
    }
    
    // ✅ Bien
    useEffect(() => {
    	if (condicion) {
    		// hacer algo
    	}
    }, [condicion]);
  2. Solo usa hooks en componentes de React o en otros hooks personalizados

       // ❌ Mal
    function normalFunction() {
    	const [count, setCount] = useState(0);
    }
    
    // ✅ Bien
    function MiComponente() {
    	const [count, setCount] = useState(0);
    }

Organizando tus Hooks

1. Agrupa hooks relacionados

   function MiComponente() {
	// 1. Hooks de estado
	const [usuario, setUsuario] = useState(null);
	const [cargando, setCargando] = useState(false);
	const [error, setError] = useState(null);

	// 2. Hooks de contexto
	const tema = useContext(TemaContext);
	const auth = useContext(AuthContext);

	// 3. Hooks de efectos
	useEffect(() => {
		// lógica de carga
	}, []);

	useEffect(() => {
		// lógica de actualización
	}, [usuario]);

	// 4. Hooks de memoización
	const datosProcesados = useMemo(() => {
		return procesarDatos(usuario);
	}, [usuario]);

	const callback = useCallback(
		() => {
			// lógica del callback
		},
		[
			/* dependencias */
		]
	);

	// 5. Hooks de refs
	const inputRef = useRef(null);
}

2. Extrae lógica compleja a hooks personalizados

   // Hook personalizado para manejar datos de usuario
function useUsuario(userId) {
	const [usuario, setUsuario] = useState(null);
	const [cargando, setCargando] = useState(true);
	const [error, setError] = useState(null);

	useEffect(() => {
		async function cargarUsuario() {
			try {
				const datos = await fetchUsuario(userId);
				setUsuario(datos);
			} catch (err) {
				setError(err);
			} finally {
				setCargando(false);
			}
		}

		cargarUsuario();
	}, [userId]);

	return { usuario, cargando, error };
}

// Uso en el componente
function PerfilUsuario({ userId }) {
	const { usuario, cargando, error } = useUsuario(userId);

	if (cargando) return <div>Cargando...</div>;
	if (error) return <div>Error: {error.message}</div>;

	return <div>Bienvenido, {usuario.nombre}!</div>;
}

Patrones Comunes y Soluciones

1. Manejo de suscripciones

   function useSuscribirse(evento) {
	useEffect(() => {
		const suscripcion = evento.subscribe();
		return () => suscripcion.unsubscribe();
	}, [evento]);
}

2. Debounce y Throttle

   function useDebounce(valor, delay) {
	const [valorDebounceado, setValorDebounceado] = useState(valor);

	useEffect(() => {
		const timer = setTimeout(() => {
			setValorDebounceado(valor);
		}, delay);

		return () => clearTimeout(timer);
	}, [valor, delay]);

	return valorDebounceado;
}

3. Formularios Controlados

   function useFormulario(valoresIniciales) {
	const [valores, setValores] = useState(valoresIniciales);

	const handleChange = useCallback((e) => {
		const { name, value } = e.target;
		setValores((prev) => ({
			...prev,
			[name]: value,
		}));
	}, []);

	const handleSubmit = useCallback(
		(e) => {
			e.preventDefault();
			// lógica de envío
		},
		[valores]
	);

	return { valores, handleChange, handleSubmit };
}

Optimización de Rendimiento

1. Evita re-renders innecesarios

   // ❌ Mal
function Componente() {
	const [count, setCount] = useState(0);
	const datos = procesarDatosComplejos(count); // Se recalcula en cada render

	return <div>{datos}</div>;
}

// ✅ Bien
function Componente() {
	const [count, setCount] = useState(0);
	const datos = useMemo(() => procesarDatosComplejos(count), [count]);

	return <div>{datos}</div>;
}

2. Memoiza callbacks

   // ❌ Mal
function Componente() {
	const handleClick = () => {
		// lógica
	};
	return <button onClick={handleClick}>Click me</button>;
}

// ✅ Bien
function Componente() {
	const handleClick = useCallback(
		() => {
			// lógica
		},
		[
			/* dependencias */
		]
	);
	return <button onClick={handleClick}>Click me</button>;
}

Tips y Trucos

  1. Usa el hook de debug para desarrollo

       function useDebug(nombre, valor) {
    	useEffect(() => {
    		console.log(`${nombre}:`, valor);
    	}, [nombre, valor]);
    }
  2. Crea hooks para lógica de UI

       function useModal() {
    	const [isOpen, setIsOpen] = useState(false);
    	const open = useCallback(() => setIsOpen(true), []);
    	const close = useCallback(() => setIsOpen(false), []);
    	return { isOpen, open, close };
    }
  3. Combina hooks para funcionalidades complejas

       function useAutenticacion() {
    	const [usuario, setUsuario] = useState(null);
    	const [cargando, setCargando] = useState(true);
    	const [error, setError] = useState(null);
    	const navigate = useNavigate();
    
    	const login = useCallback(
    		async (credenciales) => {
    			try {
    				setCargando(true);
    				const usuario = await autenticar(credenciales);
    				setUsuario(usuario);
    				navigate('/dashboard');
    			} catch (err) {
    				setError(err);
    			} finally {
    				setCargando(false);
    			}
    		},
    		[navigate]
    	);
    
    	return { usuario, cargando, error, login };
    }

Errores Comunes y Cómo Evitarlos

  1. Dependencias faltantes en useEffect

       // ❌ Mal
    useEffect(() => {
    	console.log(count);
    }, []); // Falta count en las dependencias
    
    // ✅ Bien
    useEffect(() => {
    	console.log(count);
    }, [count]);
  2. Estado inicial incorrecto

       // ❌ Mal
    const [datos, setDatos] = useState(null);
    
    // ✅ Bien
    const [datos, setDatos] = useState({
    	cargando: true,
    	error: null,
    	data: null,
    });
  3. Cleanup faltante en efectos

       // ❌ Mal
    useEffect(() => {
    	const suscripcion = evento.subscribe();
    }, []);
    
    // ✅ Bien
    useEffect(() => {
    	const suscripcion = evento.subscribe();
    	return () => suscripcion.unsubscribe();
    }, []);

Conclusión

Los hooks son una herramienta poderosa en React, pero requieren práctica y conocimiento para usarlos correctamente. Recuerda:

  • Mantén tus hooks simples y enfocados
  • Extrae lógica compleja a hooks personalizados
  • Optimiza el rendimiento cuando sea necesario
  • Sigue las reglas de los hooks
  • Usa TypeScript para mejor autocompletado y detección de errores