Welcome to Chapter 4! In the previous chapter, Local JSX Command Handler, we wrote the "Director" code. We told the CLI that when the user types stats, it should render a component called <Stats />.
However, if you try to run the code now, it will crash. Why? Because we haven't built the <Stats /> component yet!
In this chapter, we will build the actual visual interface for our command.
To understand Component Integration, think of a restaurant:
If the Waiter tried to cook the food at the table while greeting the customer, it would be a mess. By separating them:
stats.tsx) stays clean.Stats.js) only worries about visuals (colors, layout, text).
We are building a React Component. If you have used React for websites, this will look very familiar. The main difference is that instead of HTML tags like <div> or <span>, we use special tags for the terminal.
Let's create the file src/components/Stats.tsx.
We need React to understand the syntax, and we need a library called Ink. Ink is "React for the Command Line."
import React, { useState, useEffect } from 'react';
import { Text, Box } from 'ink';
Explanation:
Text: Used for displaying strings (like <span> or <p>).Box: Used for layout (flexbox) and structure (like <div>).
Remember the onDone function we passed in the previous chapter? Here, we receive it as a property called onClose.
type Props = {
// This function allows the component to close itself
onClose: () => void;
};
Explanation:
onClose function. This is the "off switch" for our application.Now we write the actual function that defines what the user sees.
export const Stats = ({ onClose }: Props) => {
// This is where our UI logic lives
return (
<Box borderStyle="round" borderColor="cyan" padding={1}>
<Text>
Welcome to your <Text color="green">Stats</Text> Dashboard!
</Text>
</Box>
);
};
Explanation:
export const Stats: We export this so the Command Handler can import it.<Box>: We wrap everything in a box. We gave it a rounded border and a cyan color.<Text>: We write our message. Notice we can nest <Text> inside <Text> to change the color of just one word!How does this actually work? Browsers have a "DOM" (Document Object Model) that React manipulates. Terminals don't have a DOM; they just process streams of text characters.
When we integrate our component, a translation process happens.
<Stats />.
A static message is nice, but we want our component to do something. Let's make it so the command closes after 3 seconds. This demonstrates how the Component uses the Integration with the Command Handler via onClose.
We will use a standard React hook: useEffect.
export const Stats = ({ onClose }: Props) => {
useEffect(() => {
// Set a timer for 3000 milliseconds (3 seconds)
const timer = setTimeout(() => {
onClose(); // Press the "Off Button"
}, 3000);
return () => clearTimeout(timer);
}, []);
return <Text>Closing in 3 seconds...</Text>;
};
What is happening here?
useEffect starts a timer.onClose() is called.Note: This section explains the library internals. You don't need to write this.
When you write <Box> or <Text>, Ink is doing heavy lifting behind the scenes to calculate layout. The terminal is a grid of character cells. Ink uses the Yoga Layout Engine (the same engine used by React Native) to calculate exactly which cell each letter should occupy.
For example, if you write this:
<Box flexDirection="column">
<Text>A</Text>
<Text>B</Text>
</Box>
Ink calculates:
A goes on Line 1.B goes on Line 2 (because flexDirection is "column").
If you changed it to flexDirection="row", Ink would calculate:
A goes on Line 1, Position 1.B goes on Line 1, Position 2.This allows us to build complex, responsive layouts in a black-and-white terminal window just like we build websites!
In this chapter, we learned:
useEffect) to manage logic inside the component.onClose prop.
Speaking of onClose, managing the lifecycle of a commandβknowing exactly when to start and when to stopβis critical for a good user experience. We don't want the app to hang forever or close too early.
Next Chapter: Lifecycle Control (onDone)
Generated by Code IQ