In the previous chapter, Terminal UI Components, we built a beautiful interface for our session command. We created the layout for a title, a QR code, and a URL.
However, right now, our component is a bit "dumb." It doesn't know what the URL is. It renders, but it's waiting for data that hasn't arrived yet.
In this chapter, we will connect our UI to the "brain" of the application using the Reactive State Hook.
Imagine you order a pizza. You want to know when it leaves the oven and when it is out for delivery.
In our session command, the Remote Session URL isn't available instantly. The CLI has to talk to a server to get it. This might take 500ms or 2 seconds.
We need a way for our UI component to:
The Solution: We use a hook called useAppState.
In a React-based application (even in the terminal), we often distinguish between two types of data:
useState): Data only this specific component cares about (e.g., "Is the dropdown open?").useAppState): Data the entire application cares about (e.g., "What is the current Session URL?", "Is the user logged in?").
The useAppState hook allows any component, anywhere in the app, to plug into that global data stream.
Let's look at how we implement this in session.tsx. We want to grab the remoteSessionUrl.
We don't want to listen to everything (we don't care about user settings or window size right now). We only want the URL.
import { useAppState } from '../../state/AppState.js';
// Inside our SessionInfo component:
const remoteSessionUrl = useAppState(state => state.remoteSessionUrl);
Explanation:
state => state.remoteSessionUrl: This is a "Selector" function. It's like going to a buffet and putting only the item you want on your plate.remoteSessionUrl changes in the background, this component will automatically re-run.
Now that we have the variable, we need to do something when it changes. We use React's useEffect for this.
import { useEffect, useState } from 'react';
// Local state to hold the generated block of text
const [qrCode, setQrCode] = useState('');
useEffect(() => {
// 1. If we don't have a URL yet, do nothing.
if (!remoteSessionUrl) return;
// 2. If we DO have it, generate the QR code
generateQRCode(remoteSessionUrl);
}, [remoteSessionUrl]); // <--- The Dependency Array
Explanation:
[remoteSessionUrl]: This tells React, "Only run this code block if remoteSessionUrl has changed."useAppState triggers a re-render. useEffect sees the new URL and generates the QR code.While we wait for the data, we should tell the user what is happening.
// Inside our JSX return statement
{isLoading ? (
<Text dimColor={true}>Generating QR code...</Text>
) : (
// Show the actual QR code lines
lines.map((line, i) => <Text key={i}>{line}</Text>)
)}
Output:
session. Terminal shows: "Generating QR code..."useAppState fires.How does the component know the data changed without us calling it manually?
The useAppState hook follows a pattern called Publish/Subscribe (or Pub/Sub).
If we looked inside ../../state/AppState.js, we would see a library (likely something like Zustand) managing this.
Here is a simplified view of what useAppState does internally:
// Simplified pseudo-code of the hook
function useAppState(selector) {
// 1. Get current value
const [value, setValue] = useState(selector(globalStore.get()));
useEffect(() => {
// 2. Subscribe to the global store
const unsubscribe = globalStore.subscribe((newState) => {
// 3. Check if the specific slice we want changed
const newValue = selector(newState);
if (newValue !== value) {
setValue(newValue); // Triggers component re-render
}
});
return unsubscribe; // Cleanup when component dies
}, []);
return value;
}
Explanation:
setValue, which forces React to refresh the UI component we built in Chapter 3.You have successfully connected your UI to the application's brain!
useAppState to listen for the remote session URL.useEffect to trigger the QR code generation only when that URL arrives.Now our application is alive. It loads data, updates the screen, and looks great. But there is one final piece of the puzzle. How does the user control the application? We told them to "press esc to close," but currently, pressing keys does nothing.
Let's fix that in the final chapter.
Next Chapter: Interactive Keybindings
Generated by Code IQ