useUserState
useUserState reads, writes, and drafts user state for the project mounted by SkyStateProvider.
Authentication Setup
User state requires an authenticated end user. Put user-state UI behind an auth gate:
tsx
import { useAuth } from '@skystate/react';
export function UserStateAuthGate({ children }: { children: React.ReactNode }) {
const auth = useAuth();
if (auth.status !== 'authenticated') {
return <button onClick={auth.loginWithRedirect}>Sign in</button>;
}
return <>{children}</>;
}See Auth and subsystem status for the full auth API.
Basic Examples
Read and update a value
tsx
import { useUserState } from '@skystate/react';
export function ThemeToggle() {
const { value: theme, set: setTheme } = useUserState('theme', 'dark');
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Theme: {theme}
</button>
);
}Draft a form before saving
tsx
import { useUserState } from '@skystate/react';
export function ProfileEditor() {
const { draft: profile } = useUserState('profile', { displayName: '' });
return (
<form
onSubmit={(event) => {
event.preventDefault();
profile.push();
}}
>
<input
value={profile.displayValue.displayName}
onChange={(event) => {
profile.set((current) => ({
...current,
displayName: event.target.value,
}));
}}
/>
<button type="submit" disabled={!profile.isPending}>Save</button>
<button type="button" onClick={profile.discard}>Discard</button>
</form>
);
}Show loading and errors
tsx
import { useUserState } from '@skystate/react';
export function UserStateGate({ children }: { children: React.ReactNode }) {
const userState = useUserState();
if (userState.status === 'loading') return <Skeleton />;
if (userState.status === 'error') {
return <div>{userState.error.message}</div>;
}
return <>{children}</>;
}API
ts
function useUserState(): SubsystemStatus
function useUserState<T = unknown>(
key: string,
): {
value: T | null;
set: (value: T | null | ((prev: T | null) => T | null)) => void;
draft: UseUserStateDraft<T | null>;
}
function useUserState<T>(
key: string,
fallback: T,
): {
value: T;
set: (value: T | ((prev: T) => T)) => void;
draft: UseUserStateDraft<T>;
}Parameters
| Parameter | Description |
|---|---|
key | Top-level user-state key such as theme, profile, or preferences. |
fallback | Value returned while user state is not ready, when the key is absent, or when the committed value is null. Also narrows value from T | null to T. |
Keys are top-level names. They cannot be empty and cannot contain / or ~.
Returns
| Call | Return |
|---|---|
useUserState() | { status: 'idle' | 'loading' | 'ready' } or { status: 'error'; error: SkyStateError }. |
useUserState<T>(key) | { value: T | null; set; draft }. |
useUserState<T>(key, fallback) | { value: T; set; draft }. |
set(value)
ts
set(value: T | ((prev: T) => T)): voidWrites a new value for the selected key. The visible value updates immediately; save errors are reported through onError and the no-argument status hook.
Draft Handle
ts
type UseUserStateDraft<T> = {
displayValue: T;
pendingValue: T | undefined;
isPending: boolean;
set: (value: T | ((displayValue: T) => T)) => void;
push: () => void;
discard: () => void;
};| Member | Description |
|---|---|
displayValue | Drafted value when present, otherwise the committed value. Safe for controlled inputs. |
pendingValue | Drafted value, or undefined when untouched. |
isPending | Whether a local draft exists. |
set(value) | Updates the local draft only. |
push() | Saves the current draft value. |
discard() | Drops the local draft value. |
Public Contract
| Contract | Detail |
|---|---|
| Auth required | User state reads and writes require a signed-in end user. |
| Top-level keys | Keys are top-level names, not nested paths. |
| Local update | set updates the visible value immediately. |
| Status | Use useUserState() with no arguments for loading and error state. |
| Errors | Validation, auth, permission, quota, and configuration issues surface through onError and the status hook. |
| Metadata | Use the console, CLI, or REST API for inspection, audit, or history metadata. |