streamich/react-use · error · Error
states expected to be an object or array, got ${typeof state
Error message
states expected to be an object or array, got ${typeof states} What it means
Thrown by useMultiStateValidator when its first argument `states` is not of type 'object'. The hook expects a collection (an array or a record/object) of states to validate together, and rejects primitives up front. Caveat: the check is `typeof states !== 'object'`, and because typeof null === 'object', passing null bypasses this guard and will surface later as a different failure — the intended inputs are arrays and plain objects only.
Source
Thrown at src/useMultiStateValidator.ts:17
import { useCallback, useEffect, useRef, useState } from 'react';
import { StateValidator, UseStateValidatorReturn, ValidityState } from './useStateValidator';
export type MultiStateValidatorStates = any[] | { [p: string]: any } | { [p: number]: any };
export type MultiStateValidator<V extends ValidityState, S extends MultiStateValidatorStates> =
StateValidator<V, S>;
export function useMultiStateValidator<
V extends ValidityState,
S extends MultiStateValidatorStates
>(
states: S,
validator: MultiStateValidator<V, S>,
initialValidity: V = [undefined] as V
): UseStateValidatorReturn<V> {
if (typeof states !== 'object') {
throw new Error('states expected to be an object or array, got ' + typeof states);
}
const validatorInner = useRef(validator);
const statesInner = useRef(states);
validatorInner.current = validator;
statesInner.current = states;
const [validity, setValidity] = useState(initialValidity as V);
const validate = useCallback(() => {
if (validatorInner.current.length >= 2) {
validatorInner.current(statesInner.current, setValidity);
} else {
setValidity(validatorInner.current(statesInner.current));
}
}, [setValidity]);
View on GitHub (pinned to fbe99c6327)
Solutions
- Pass states as an array or object: useMultiStateValidator([fieldA, fieldB], validator) or useMultiStateValidator({a, b}, validator).
- If the value can be undefined, default it to an empty structure: useMultiStateValidator(states ?? [], validator).
- Wrap a single state before passing: useMultiStateValidator([singleState], validator).
- Do not pass null — although it slips past this check, it is not a valid states collection; use {} or [] instead.
Example fix
// before useMultiStateValidator(email, validateEmail); // email is a primitive string useMultiStateValidator(maybeStates, validator); // maybeStates may be undefined // after useMultiStateValidator([email], validateEmail); useMultiStateValidator(maybeStates ?? [], validator);
Defensive patterns
Strategy: type-guard
Validate before calling
function useMultiStateValidatorSafe<V extends ValidityState, S extends MultiStateValidatorStates>(
states: S,
validator: MultiStateValidator<V, S>,
initialValidity?: V
) {
if (states === null || typeof states !== 'object') {
throw new Error('states must be a non-null object or array');
}
return useMultiStateValidator(states, validator, initialValidity);
}
// or coerce at the call site:
const safeStates = states ?? {};
useMultiStateValidator(safeStates, validator); Type guard
// Accept arrays and plain objects; explicitly reject null (typeof null === 'object').
const isMultiState = (v: unknown): v is MultiStateValidatorStates =>
(Array.isArray(v) || (typeof v === 'object' && v !== null));
if (isMultiState(states)) {
useMultiStateValidator(states, validator);
}
// else: handle the primitive case before calling Try / catch
// The hook throws during render; wrap the consumer in a boundary rather than // try/catching the hook call, and fix the input via the type guard above.
Prevention
- Always wrap a single state in an array ([state]) when migrating from useStateValidator.
- Default optional states to [] or {} (states ?? []) before passing.
- Never pass null — it passes the typeof check but is not a valid collection.
- Type the parameter as MultiStateValidatorStates at the source so TypeScript catches primitives.
When it happens
Trigger: Calling useMultiStateValidator(states, validator, initialValidity) where states is a primitive (string, number, boolean, undefined, symbol, bigint). For example, passing a single state value instead of wrapping it in an array/object, or passing an optional prop that is undefined.
Common situations: Migrating from useStateValidator (single state) and forgetting to wrap the state in an array; passing an optional/derived prop that can be undefined; copy-paste where a primitive counter/flag is handed in directly.
Related errors
- useLocalStorage key may not be falsy
- State '${state}' is not a valid state (does not exist in sta
- Capacity has to be greater than 1, got '${capacity}'
- useReducerContext must be used inside a ReducerProvider.
- Router route must be a string.
AI-assisted analysis of streamich/react-use@fbe99c6327 (2026-08-12).
Data as JSON: /api/errors/485cd1c83ac6de73.
Report an issue: GitHub.