In the previous chapter, Terminal UI Rendering, we built the visual layer of our CLI. We learned how to paint pixels using components like <Box> and <Text>.
However, a pretty interface is useless if it doesn't remember anything.
Imagine you are building the Doctor screen (a diagnostic tool). It needs to know:
If we stored this data inside the Doctor component, no other screen (like the SessionPicker) could access it. We would have to pass data up and down a huge chain of components (a messy process called "prop drilling").
The Solution: We create a "Central Nervous System"โa global Application State. It lives outside your components, holding data that needs to be shared everywhere.
Let's look at the Doctor screen (Doctor.tsx). It acts as a health check for the system. To do its job, it needs to read data from the global state without asking its parent component for it.
We use a specific pattern called a Hook to "hook into" this global brain.
Think of the Store as a secure database living in your computer's memory. It holds the "Truth" about the application.
useAppState)This is the phone line. Any component can pick up this phone and ask the Store for information.
When you call the store, you don't want the entire database. You only want specific pieces. We use a "selector function" to pick exactly what we need.
Let's see how Doctor.tsx retrieves the list of agents and tools.
First, we import the hook that connects us to the state.
// Inside Doctor.tsx
import { useAppState } from '../state/AppState.js';
We call the hook and pass a function. This function receives the entire state (s) and returns just the part we care about.
export function Doctor({ onDone }: Props) {
// "Hey Store, give me just the agentDefinitions"
const agentDefinitions = useAppState(s => s.agentDefinitions);
// "Hey Store, give me the MCP tools list"
const mcpTools = useAppState(s => s.mcp.tools);
// ... rest of component
}
Why do we do s => s.agentDefinitions?
This is a performance optimization. If the application changes the "Theme Color" in the state, the Doctor screen shouldn't re-render because it doesn't care about colors. It only re-renders if agentDefinitions changes.
Now that we have mcpTools, we can use it just like a normal local variable to render our UI.
// Inside Doctor.tsx
const tools = mcpTools || [];
return (
<Box flexDirection="column">
<Text>Found {tools.length} tools available.</Text>
</Box>
);
Reading data is half the battle. We also need to change it. Let's look at ResumeConversation.tsx. When a user picks a session to resume, we need to update the global state to reflect the active agent.
We use a helper hook called useSetAppState.
// Inside ResumeConversation.tsx
import { useSetAppState } from '../state/AppState.js';
export function ResumeConversation() {
const setAppState = useSetAppState();
// ... later, inside an event handler
}
When the user selects a log, we update the state:
// Inside onSelect function
setAppState(prev => ({
...prev,
// We update the active agent to match the resumed session
agent: resolvedAgentDef?.agentType
}));
Explanation:
prev represents the current state before the update....prev means "keep everything else exactly the same."agent: overwrites just the agent property.
How does a change in ResumeConversation instantly update the Doctor screen?
Doctor calls useAppState, it subscribes to the store.ResumeConversation, calling setAppState.Doctor realizes the data changed and re-renders automatically.
Sometimes we need to combine global state with local logic. In Doctor.tsx, we use a useEffect to calculate warnings based on the global state.
// Inside Doctor.tsx
useEffect(() => {
// We read the global state values we extracted earlier
const { activeAgents, allAgents } = agentDefinitions;
// We run a heavy calculation to check for errors
checkContextWarnings(tools, { activeAgents, allAgents })
.then(warnings => {
// We save the result to LOCAL state, not global
setContextWarnings(warnings);
});
}, [tools, agentDefinitions]);
Key Takeaway:
agentDefinitions): The raw data shared by everyone.contextWarnings): The specific result calculated just for this screen.In this chapter, we learned:
useAppState to read data.s => s.data) to pick only what we need and improve performance.useSetAppState to update the global data.Now that our screens can share data, we need to understand where that data comes from. How do we define what an "Agent" is, and how do we load them into this state?
Next Chapter: Agent & Tool Configuration
Generated by Code IQ