Welcome to the final chapter of the Hierarchical Tree Selector tutorial!
In the previous chapter, Auto-Numbering List Container, we built a smart parent component that knows how to count its children and calculate the correct numbers (like " 1." or "10.").
However, a parent component cannot display itself. It needs a child component to actually render the text.
This chapter introduces the Context-Aware List Item: a component that listens to the parent to find out its number and then displays your content.
Imagine you are writing a list of files in your code:
// The "Hard Way"
<Item label="1. index.ts" />
<Item label="2. utils.ts" />
<Item label="3. styles.css" />
If you delete utils.ts, you have to manually rename styles.css to be number "2.". If you have a list of 50 items, this is a nightmare.
We want to write code where the item doesn't know its own number. It should just ask: "Hey Parent, what number am I?"
We will build a component called OrderedListItem. It is "dumb" in a good way:
In React, if a Parent uses Context.Provider (the Broadcaster), the Child uses useContext (the Receiver).
Analogy: Think of the Parent Component as a Radio Station broadcasting a song. The Child Component is the Radio. The Radio doesn't create the music; it just plays whatever is on the frequency.
Let's see how simple our final code looks when using this component.
Input:
<OrderedList>
{/* We don't pass any numbers here! */}
<OrderedList.Item>index.ts</OrderedList.Item>
<OrderedList.Item>package.json</OrderedList.Item>
</OrderedList>
Output: The item automatically receives the formatted string from the parent.
1. index.ts
2. package.json
Let's visualize the communication flow. The OrderedListItem is completely passive until it receives data.
Let's look at the code for OrderedListItem.tsx. It is surprisingly simple because all the hard math was done in the previous chapter.
First, we need to access the same Context that the parent defined.
// OrderedListItem.tsx
import React, { useContext } from 'react';
import { Box, Text } from '../../ink.js';
// We create/export this so the Parent knows where to broadcast
export const OrderedListItemContext = React.createContext({
marker: ''
});
Inside the component, we use the useContext hook. This grabs the data sent by the nearest parent provider.
export function OrderedListItem({ children }) {
// Receive the "marker" (e.g., " 1.") from the parent
const { marker } = useContext(OrderedListItemContext);
// ... rendering logic
}
Now we just arrange the marker and the children side-by-side using a Box.
// Inside OrderedListItem function returns:
return (
<Box gap={1}>
{/* Display the number in a dim color so it's subtle */}
<Text dimColor>{marker}</Text>
{/* Display the actual content (the file name) */}
<Box flexDirection="column">
{children}
</Box>
</Box>
);
Why
dimColor? In terminal UIs, it is good practice to make metadata (like line numbers) less bright than the actual content. This helps the user focus on the file names.
Congratulations! You have built all the pieces of the Hierarchical Tree Selector.
Let's recap how the data flows through the entire system we built over these 5 chapters:
TreeNode).Set.OrderedList container calculates the line numbers for that flat array.OrderedListItem (this chapter) displays the final line.When you combine these 5 concepts, you get a professional, interactive CLI component:
1. Project
2. βΎ src
3. | components
4. | βΎ Button
5. | | index.ts
6. package.json
You have successfully built a complex UI abstraction from scratch!
We started with a difficult problem (displaying nested data in a flat terminal) and solved it by breaking it down into small, manageable responsibilities:
These patternsβFlattening, Context Providers, and Decoupled Itemsβare used everywhere in professional UI development, from web apps to terminal tools.
Thank you for following the Hierarchical Tree Selector tutorial series. Happy coding!
Generated by Code IQ