# Purpose of `src/reducers/appReducer.ts` in Coco App Global State Management

> Discover how src/reducers/appReducer.ts manages Coco App's transient UI state using a predictable Redux-style reducer and React's useReducer hook.

- Repository: [INFINI Labs/coco-app](https://github.com/infinilabs/coco-app)
- Tags: internals
- Published: 2026-03-04

---

**The [`src/reducers/appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/src/reducers/appReducer.ts) file defines the central Redux-style reducer that manages all transient UI state for Coco App through a predictable state container pattern using React's `useReducer` hook.**

The [`src/reducers/appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/src/reducers/appReducer.ts) module serves as the cornerstone of global UI state management in the Coco App codebase. It implements an immutable state reducer pattern that centralizes control over interface flags like chat mode, search activation, and loading indicators. This architecture ensures that components across the application share a single source of truth for UI behavior while maintaining predictable, traceable state transitions.

## Core Responsibilities of [`appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/appReducer.ts)

### Defining the Global State Shape

The `AppState` type declared in [`src/reducers/appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/src/reducers/appReducer.ts) establishes the contract for all UI-related data. This interface enumerates boolean flags and string values that drive interface rendering, including chat mode status, input text content, loading spinners, typing indicators, search activation, deep-think mode, and MCP (Model Context Protocol) states. By codifying these fields in a single type definition, the reducer provides compile-time safety and autocomplete support for all state accesses.

### Action Type Definitions

State transitions are governed by the `AppAction` discriminated union, which explicitly lists every permissible mutation as a typed action object. Key action types include:

- `SET_CHAT_MODE` – toggles between chat and standard interfaces
- `TOGGLE_SEARCH_ACTIVE` – flips the search visibility flag
- `SET_INPUT` – updates the current text input value
- `SET_LOADING` and `SET_TYPING` – control asynchronous operation indicators
- `SET_DEEP_THINK_ACTIVE` – enables the deep-think reasoning feature

Each action carries a specific payload type, ensuring that developers cannot accidentally dispatch a string where a boolean is expected.

### Immutable State Transitions

The `appReducer` function implements the classic reducer pattern by switching on `action.type` and returning a new immutable state object for each case. The implementation spreads the existing state (`...state`) and updates only the relevant slice, preventing direct mutation of the previous state reference. If an unknown action type is dispatched, the reducer returns the current state unchanged, safeguarding against accidental data loss from typos or unhandled actions.

## State Initialization and Defaults

The `initialAppState` constant bootstraps the reducer with sensible defaults while respecting user preferences from persistent storage. Rather than hard-coding initial values, the reducer imports `useStartupStore` from [`src/stores/startupStore.ts`](https://github.com/infinilabs/coco-app/blob/main/src/stores/startupStore.ts) to hydrate the state with the user's cached `defaultStartupWindow` preference. This design separates transient UI state (managed by the reducer) from persistent configuration (managed by Zustand stores), allowing the app to restore the user's preferred window mode (chat or standard) on every launch while keeping volatile UI flags in memory only.

## Integration with React Components

Components consume the reducer through React's `useReducer` hook, creating localized state instances that remain synchronized through the dispatch mechanism. The **SearchChat** component in [`src/components/SearchChat/index.tsx`](https://github.com/infinilabs/coco-app/blob/main/src/components/SearchChat/index.tsx) demonstrates this pattern:

```tsx
import { appReducer, initialAppState } from "@/reducers/appReducer";

const [state, dispatch] = useReducer(
  appReducer,
  customInitialState ?? initialAppState
);

```

This instantiation provides the component with a `state` object containing all UI flags and a `dispatch` function for triggering updates. The reducer is also integrated with [`src/hooks/useSyncStore.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useSyncStore.ts), which synchronizes UI state changes with the global Zustand stores, bridging the gap between transient reducer state and persistent application data.

## Practical Usage Examples

Components dispatch typed actions to modify specific UI flags. These patterns demonstrate the reducer's API surface:

### Toggling Search Mode

```tsx
dispatch({ type: "TOGGLE_SEARCH_ACTIVE" });

```

This flips `state.isSearchActive` between `true` and `false`, immediately updating any search overlay components subscribed to the state.

### Switching Chat Modes

```tsx
dispatch({ type: "SET_CHAT_MODE", payload: true });   // enters chat mode
dispatch({ type: "SET_CHAT_MODE", payload: false });  // exits chat mode

```

Updates both `state.isChatMode` and `state.isTransitioned` to coordinate animation states during mode switches.

### Controlling Text Input

```tsx
dispatch({ type: "SET_INPUT", payload: newText });

```

Stores the current user input in `state.input`, enabling real-time synchronization between input fields and suggestion panels.

### Managing Loading Indicators

```tsx
dispatch({ type: "SET_LOADING", payload: true });
dispatch({ type: "SET_TYPING", payload: false });

```

Drives visual spinners and typing animations by updating `state.isLoading` and `state.isTyping` independently.

### Enabling Deep-Think Mode

```tsx
dispatch({ type: "SET_DEEP_THINK_ACTIVE", payload: true });

```

Activates the advanced reasoning interface by setting `state.isDeepThinkActive` to `true`.

## Summary

- **[`src/reducers/appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/src/reducers/appReducer.ts)** implements a Redux-style reducer that centralizes all transient UI state for Coco App into a single `AppState` object.
- The **discriminated union** pattern used for `AppAction` provides compile-time type safety for all state mutations.
- **Immutable updates** are enforced through object spreading, ensuring predictable state transitions that React can optimize.
- **Integration with `useStartupStore`** allows the reducer to initialize with user preferences while keeping volatile UI flags separate from persistent storage.
- Components like **SearchChat** consume the reducer via `useReducer`, dispatching typed actions to synchronize interface behavior across the application.

## Frequently Asked Questions

### How does [`appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/appReducer.ts) differ from Zustand stores in Coco App?

[`src/reducers/appReducer.ts`](https://github.com/infinilabs/coco-app/blob/main/src/reducers/appReducer.ts) manages transient UI flags (like loading states and input text) that reset between sessions, while Zustand stores in [`src/stores/startupStore.ts`](https://github.com/infinilabs/coco-app/blob/main/src/stores/startupStore.ts) handle persistent user preferences (like default window modes) that survive application restarts. The reducer provides immediate, synchronous updates for interface responsiveness, whereas Zustand handles long-term configuration persistence.

### Can I use `appReducer` outside of the SearchChat component?

Yes. Any React component can import `appReducer` and `initialAppState` from `@/reducers/appReducer` and instantiate its own state container with `useReducer`. However, for truly global state sharing across unrelated components, you would need to lift the reducer state to a common parent or context provider, as each `useReducer` call creates an isolated state instance.

### What happens if I dispatch an unknown action type to `appReducer`?

The reducer includes a default case that returns the current state unchanged. This defensive pattern prevents runtime crashes and state corruption when unrecognized actions are dispatched, making the state management resilient to future action type additions or development-time typos.

### How does the reducer handle the deep-think feature state?

The reducer tracks deep-think activation through the `isDeepThinkActive` boolean flag in `AppState`. Components dispatch `SET_DEEP_THINK_ACTIVE` with a boolean payload to toggle this mode. This state drives UI rendering for the advanced reasoning interface while keeping the flag localized to the current session, as deep-think preferences are not persisted to the startup store by default.