React
Reglas de los Hooks
Explica por qué los Hooks deben llamarse en el nivel superior de componentes o custom hooks y cómo React depende de un orden estable entre renders.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
React
Explica por qué los Hooks deben llamarse en el nivel superior de componentes o custom hooks y cómo React depende de un orden estable entre renders.
if (isLoggedIn) {
const [user, setUser] = useState(null); // Incorrecto
}React relaciona cada llamada con su posición en el orden de ejecución. Condiciones, bucles o retornos tempranos pueden cambiar ese orden.
Los Hooks se llaman desde:
No desde funciones JavaScript arbitrarias.
const user = useUser();
if (!isLoggedIn) return <Login />;O diseña el hook para aceptar una condición explícita.
Una función que llama Hooks debe comenzar con use. Esto permite a las herramientas reconocerla y aplicar las reglas.
No llames Hooks desde callbacks de eventos, loaders externos o funciones de utilidad. Pasa los valores necesarios como argumentos.
El plugin oficial detecta violaciones de orden y dependencias. No desactives reglas para ocultar un diseño confuso.
La API use tiene reglas particulares y puede aparecer en condiciones o bucles, pero sigue necesitando ejecutarse dentro de un componente o Hook y no debe envolverse en try/catch.
.map.El orden estable permite que React asocie estado y efectos con cada llamada.
React no identifica una llamada por el nombre de la variable. La relaciona con su posición dentro de la secuencia de Hooks de la instancia.
render 1
1. useState(name)
2. useState(age)
3. useEffect(...)
render 2
1. useState(name)
2. useState(age)
3. useEffect(...)Si una condición elimina la segunda llamada, las posiciones posteriores dejan de representar la misma memoria.
function Profile({ includeAge }: { includeAge: boolean }) {
const [name, setName] = useState("");
if (includeAge) {
const [age, setAge] = useState(0); // incorrecto
}
const [city, setCity] = useState("");
}Cuando includeAge cambia, React ya no puede asociar city con la celda correcta.
El problema también aparece cuando un Hook queda después de un retorno condicional:
function UserPanel({ user }: Props) {
if (!user) return <Login />;
const [tab, setTab] = useState("profile"); // incorrecto si user puede cambiar
}Llama Hooks antes del retorno o divide componentes:
function UserPanel({ user }: Props) {
if (!user) return <Login />;
return <AuthenticatedPanel user={user} />;
}AuthenticatedPanel tiene una secuencia estable durante su propia vida.
items.map((item) => {
const [open, setOpen] = useState(false); // incorrecto
});El número y orden dependen de la colección. Extrae un componente:
items.map((item) => <ItemRow key={item.id} item={item} />);Cada ItemRow posee su propia secuencia estable.
Tampoco llames Hooks dentro de handlers:
function handleClick() {
const value = useContext(Context); // incorrecto
}Lee el valor en el componente y úsalo desde la closure del handler.
Una función común puede ejecutarse fuera de React, por lo que no dispone de una instancia actual:
function getUserPreferences() {
return useContext(PreferencesContext); // incorrecto
}Convierte la dependencia en argumento:
function formatPreferences(preferences: Preferences) {
// función pura
}O crea un Hook cuando la función representa lógica reactiva:
function useFormattedPreferences() {
const preferences = useContext(PreferencesContext);
return formatPreferences(preferences);
}const content = UserCard({ user }); // incorrectoEsto ejecuta sus Hooks dentro de la secuencia del padre y elimina su identidad como componente. Renderiza:
const content = <UserCard user={user} />;React crea una instancia con su propia memoria y reglas.
El prefijo use no es decoración. Permite que humanos, linter y Compiler reconozcan que la función participa en las Rules of Hooks.
No nombres useFormatPrice a una función pura que no usa Hooks; puede sugerir restricciones inexistentes. Usa formatPrice.
La API use puede llamarse en condiciones y loops porque su mecanismo no depende de la misma secuencia de celdas que otros Hooks:
if (shouldRead) {
const value = use(resource);
}Aun así:
try/catch para interceptar suspensión.El plugin oficial incluye reglas más amplias que el orden básico. Puede detectar:
No desactives una regla sin comprender qué garantía protege. Cuando el linter “molesta”, a menudo revela una dependencia o responsabilidad mal modelada.
No pases Hooks como valores ni los selecciones dinámicamente:
const useData = mode === "remote" ? useRemoteData : useLocalData;
const data = useData();Aunque parezca estable, dificulta análisis y puede cambiar la secuencia. Extrae componentes o llama ambos bajo un diseño explícito cuando sea válido.
Crear una función componente durante render produce un tipo nuevo y puede afectar identidad, además de ocultar Hooks:
function Parent() {
const Child = createChildComponent(config);
return <Child />;
}Prefiere un tipo estable que reciba config como prop.
Causas comunes:
react y renderer.Revisa el stack, lockfile, peer dependencies y salida de npm ls react o la herramienta equivalente.
use tiene reglas particulares, no una excepción general.Component() es diferente de <Component />?Invalid hook call?useRef y valores persistentes sin render aplica las reglas para conservar información mutable que no debe provocar actualizaciones visuales.