Skip to main content

Hooks

Import React hooks from react (aliased to @kog/react), as named imports or through the default export (import React from 'react', then React.useState). Import Kog-specific hooks from @kog/react or @kog/ui, as noted. Semantics differ from React in six intentional ways, so read Thinking in Kog first.

React-compatible​

HookNotes in Kog
useState(initial)Returns [value, setValue]. Writes are synchronous; UI flush batched. Functional updates supported.
useReducer(reducer, init)As React.
useEffect(fn, deps?)Runs after mount; cleanup on unmount/re-run. With a deps array it behaves as in React: it re-runs only when an element of the array changes (Object.is), and [] means mount-only. Reads inside fn always see current state, so an unlisted value is never stale, but changing it doesn't re-run the effect. Without a deps array the effect is auto-tracked: it re-runs when any reactive value it reads changes. The cleanup also reads state live: on a re-run it sees the new value, where React's sees the old one. To keep the old value, copy it to a const inside fn. List props by field ([props.id]): the props object itself never changes, and the compiler warns if you list it or an alias of it (const p = props).
useLayoutEffect(fn, deps?)Same as useEffect, deps included (it has no separate pre-frame flush).
useMemo(fn, deps)A true derived value: it recomputes when its inputs change and is cached otherwise. It is optional for pure derivations, because the compiler automatically keeps a bare const computed from state/props in the body live. useMemo is the same memo spelled explicitly. It is required when the compiler refuses an initializer it can't prove side-effect-free.
useCallback(fn)Identity: functions are already stable. Kept for compatibility.
useRef(initial)Stable { current }, non-reactive. Pass it as ref={...} on a built-in element (View, Text, TextInput, …) and .current is the native widget handle while the element is mounted, null after. Callback refs (ref={(h) => …}) work too: called with the handle at mount and with null at unmount.
createContext / useContextThe provider's value is read once at mount. For a changing context, provide an accessor (value={() => theme}) and call it where you read it; consumers then update without re-running.
useImperativeHandle(ref, create)Sets ref (object or callback) to create() once at mount and clears it at unmount.
useSyncExternalStoreShim over a store subscription.
useId()A string unique within the app, stable for the component's lifetime.

Not present: useTransition, useDeferredValue, Suspense-related hooks. There is no concurrency to schedule around; updates are already minimal.

Other react exports​

ExportNotes in Kog
React (default export)The namespace object: React.useState, React.memo, React.Fragment, and the rest. React.useState(…) compiles exactly like the named import.
memo(Component)Returns the component unchanged. Components mount once and never re-render, so there is nothing to skip; areEqual is never called.
forwardRef(render)Passes the ref prop to render(props, ref), so a component can hand a parent's ref to one of its built-in elements. memo and forwardRef (also as React.memo/React.forwardRef) can wrap a component defined inline, in any nesting.
Fragment<>…</>, <Fragment>…</Fragment> and <React.Fragment> group children without a wrapper widget. A fragment can't be a list row (a .map() or FlatList row); wrap the row in a View with a key instead.

React Native hooks and APIs​

These come from 'react-native', as in React Native. The react-native reference lists every export.

Hook or APINotes in Kog
useWindowDimensions(){ width, height, scale, fontScale } in logical pixels. The display never changes size, so the values are fixed for the app's lifetime.
useColorScheme()'light' | 'dark', read from Appearance, or from the nearest ThemeProvider that sets scheme. Expressions that use it update when the scheme changes, without remounting. There is one implementation; the react, react-native and @kog/ui imports are the same function.
AppearancegetColorScheme(), setColorScheme(scheme) and addChangeListener(fn). A device has no OS theme, so the app sets the scheme; it starts as 'dark'.
AnimateduseAnimatedValue(0) or useRef(new Animated.Value(0)).current, then Animated.timing(value, { toValue, duration, easing }).start(({ finished }) => …). Also spring, decay, sequence, parallel, stagger, loop, delay, numeric and angle interpolate, and the Animated.View/Text/Image family. Animations run in the native engine, with no JavaScript per frame. Animated.event, the arithmetic operators and color outputs throw. See Coming from React Native for the details.
EasingThe curves for Animated.timing, such as Easing.linear, Easing.inOut(Easing.quad) and Easing.bezier(…). Every React Native curve runs exactly in the native engine.
KeyboardKeyboard.dismiss(), Keyboard.isVisible(), Keyboard.metrics() and Keyboard.addListener('keyboardDidShow', fn). The on-screen keyboard opens when a TextInput is focused. As in React Native, the show event arrives on the next turn, so a listener added in useEffect after <TextInput autoFocus> still gets it.
DimensionsDimensions.get('window') is the same size useWindowDimensions returns.

Timing​

HookSignature
useInterval(fn, ms | null)Auto-cleans; ms may be reactive; null pauses.
useTimeout(fn, ms | null)Same contract.
useAnimatedValue(initial, config?)From @kog/react: Kog's lower-level animated value ('react-native' also exports useAnimatedValue, which returns an Animated.Value). Bind it to a widget prop (<View width={anim} />), then call anim.animate(to, { duration, easing }, onDone). It runs in the native animation engine, with zero JS per frame. Values are in the prop's units: px for size/position, and 0–1 for opacity (the same scale as static opacity). New code can use Animated from 'react-native' instead.

Device & app​

HookReturns
useDisplay(){ width, height, shape, safeRect, insetAt(y), contains(x,y), dpi, rotation }: display geometry in logical px, as a snapshot (it cannot change at runtime). On shaped glass (the round Waveshare 1.46B, notched panels, arbitrary outlines) safeRect is the largest rectangle fully on the glass and insetAt(y) gives the usable { min, max } x-extent of a row (the chord, on a circle), or null where there is no glass, for shape-hugging layouts. dpi and rotation are static placeholders today; don't build on them. Pair with <SafeAreaView> from 'react-native' for the common "keep my content on the glass" case.
useDevice(){ heapFree, battery, rssi }. battery is the live battery level (0..1, or -1 when the board has no gauge), the same value as useBatteryLevel() from expo-battery. const { battery } = useDevice() in a component body stays current (the compiler treats the hook as a live value), and the battery watch is released on unmount. heapFree and rssi are still 0; read WiFi for link quality.
useStorage(key, default)useState-shaped; v1 is in-memory only (NVS persistence is a follow-up).
useFocusEffect(fn) / useIsFocused()Route focus lifecycle. In Kog, focus and mount are the same event: a route that is not focused is not mounted. So useFocusEffect runs on mount, its cleanup is the blur handler, and useIsFocused() is always true. Kept for React Navigation-shaped source.

Routing​

useNavigate, useLocation, useParams, useSearchParams, useMatch, useResolvedPath, useOutletContext, and useNavigationType all come from @kog/router and are documented in the Router reference.

Hardware​

useButton, useDigitalRead, useDigitalWrite, useAnalogRead, and usePWM are documented in Hardware. Read hooks return accessors in v1 (call them: mv(), pressed()); useDigitalWrite is [value, setValue] like useState. One caveat: derive from accessors where you read them, not in the body. const volts = mv() / 1000 at the top level freezes at its mount-time value, because the compiler cannot see that mv() is reactive (it looks like any other call). Call it inline ({(mv() / 1000).toFixed(2)}) or wrap the derivation in useMemo; the no-reactivity-loss lint flags the frozen form.