Создаем CLI-приложение с помощью React.js +4


Приложения с интерфейсом в виде командной строки (Command-Line Interface — CLI) стали популярными в экосистеме разработчиков по целому ряду причин. Самые банальные из них — это простота использования (CLI) и то, что многие важнейшие инструменты разработки представляют из себя терминальные приложения или предоставляют интерфейс командной строки, и многие разработчики уже к ним привыкли.

По мере того, как сложность и функциональность этих терминальных приложений растет, также растет и потребность в простоте и быстроте их создания.

В нашей предыдущей статье мы рассказали вам о том, как создать CLI-приложение с помощью Node.js. Один из ключевых выводов после построения интерфейса командной строки с использованием Node.js заключался в том, что делать это достаточно сложно и утомительно.

React же упрощает создание мощных и очень интерактивных CLI-приложений. В этой статье мы реализуем командную строку с помощью React.js вместо Node.js и увидим разницу.

Что от вас требуется:

  • Базовое понимание JavaScript.

  • Базовые знания React.js.

  • Базовые знания npm и/или yarn.

Почему React.js, а не Node.js?

React освобождает нас от всех хлопот по синтаксическому анализу (парсингу) аргументов, выполняя их в фоновом режиме. React также позволяет рендерить компоненты в терминале, так как вы бы делали это в браузере.

Чтобы реализовать интерфейс командной строки с помощью React, мы будем использовать библиотеку под названием INK, которая значительно упростит нашу работу. Ink также позволяет вам использовать flexbox, то есть нам больше не нужно полагаться на раскрашенные стринговые выводы, как в Node.js.

Для примера вот несколько популярных приложений, сделанных с помощью React и Ink:

  • Jest

  • Gatsby

  • Prisma

  • Typescript

  • Twilio SIGNAL

Начало работы с React INK

Ink — это фреймворк React.js, который значительно упрощает утомительную задачу создания CLI-приложений. По сравнению с Node.js Ink не требует от вас особого обучения работе с ним. Если вы знакомы с React, тогда вы готовы.

Начнем с создания простого Hello World приложения. Для этого нам понадобятся React и Ink из нашего npm. Чтобы упростить нашу работу, ink поставляется с командой для бутстрапа React CLI-приложения.

Введите в терминале:

mkdir section-example && cd section-example

npx create-ink-app

Последняя команда создает исполняемый файл для нашего приложения. Для завершения этого процесса может потребоваться некоторое время. Когда вы запустите node cli в терминале, он должен вернуть следующее:

image
image

Вот она, ваша первая командная строка на основе React. Чтобы добиться этого в Node.js, нам потребовалось бы написать много кода, и это заняло бы больше времени, не говоря уже о большем количестве задействованных библиотек.

Простой проект

Давайте продолжим работу над чуть более сложным проектом — это поможет вам понять элементы и структуру проекта React ink. Мы будем работать в файле ui.js. Файл входа для приложения — cli.js..

Код будет выглядеть примерно так:

"use strict";
const React = require("react");
const { Text } = require("ink");

const App = ({ name = "Stranger" }) => (
	<Text>
		Hello, <Text color="green">{name}</Text>
	</Text>
);

module.exports = App;

Сначала мы импортируем React из пакета react. Затем мы импортируем элемент Text, который поставляется с пакетом ink. У нас также есть функция, которая принимает на вход имя и рендерит его. Давайте создадим простое CLI-приложение, которое принимает страну в качестве входных данных. Затем оно возвращает некоторую информацию об этой стране в виде таблицы.

Для этого нам понадобится пакет npm под названием world-countries-capitals, который содержит информацию о странах.

Начнем с ввода данных пользователем. Для этого нам понадобится ввод текста (text input). К счастью для нас, ink уже предоставляет для этого пакет. Просто запустите:

npm install ink-text-input

Мы импортируем и используем ввод текста в терминале в нашем ui.js. Мы также будем использовать хук React useState для хранения значения нашей страны и обработки изменений ее названия. Проще говоря, думайте о хуках useState как о способе работы с переменными в React. Чтобы узнать больше о хуках React, я рекомендую почитать эту документацию.

Теперь наш код будет выглядеть так:

"use strict";
const React = require("react");
const { Box } = require("ink");
const TextInput = require("ink-text-input").default;

const App = () => {
	const [country, setCountry] = React.useState("");

	return (
		<Box>
			<TextInput
				placeholder="Enter your country..."
				value={country}
				onChange={setCountry}
			/>
		</Box>
	);
};

module.exports = App;

При запуске node cli в терминале, у вас должна появиться возможность ввести название страны.

Нам нужно будет искать страну в режиме реального времени и отображать результаты в таблице. Для этого мы вызовем npm-пакет world-countries-capitals. Мы будем использовать еще один хук React useEffect для извлечения наших данных и обновления компонента по мере его рендеринга. Давайте реализуем это.

Сначала установим и импортируем пакет.

Введите в терминале:

   npm i world-countries-capitals

Мы импортируем этот пакет вверху нашего файла:

const wcc = require("world-countries-capitals");

Мы создадим несколько переменных для хранения данных, которые мы получаем от хука useEffect, используя useState. Они пригодятся при обновлении таблицы в реальном времени.

const [capital, setCapital] = React.useState("");
const [currency, setCurrency] = React.useState("");
const [phone, setPhone] = React.useState("");

Наконец, мы заполняем наши переменные информацией из npm-пакета. В итоге наш хук useEffect будет выглядеть так:

React.useEffect(() => {
	const getCountry = wcc.getCountryDetailsByName(country);
	setCapital(getCountry[0].capital);
	setCurrency(getCountry[0].currency);
	setPhone(getCountry[0].phone_code);
});

На данный момент наш код, включая хук useEffect, будет выглядеть следующим образом:

"use strict";
const React = require("react");
const { Box } = require("ink");
const TextInput = require("ink-text-input").default;
const wcc = require("world-countries-capitals");

const App = () => {
	const [country, setCountry] = React.useState("");
	const [capital, setCapital] = React.useState("");
	const [currency, setCurrency] = React.useState("");
	const [phone, setPhone] = React.useState("");

	React.useEffect(() => {
		const getCountry = wcc.getCountryDetailsByName(country);
		setCapital(getCountry[0].capital);
		setCurrency(getCountry[0].currency);
		setPhone(getCountry[0].phone_code);
	});
	return (
		<Box>
			<TextInput
				placeholder="Enter your country..."
				value={country}
				onChange={setCountry}
			/>
		</Box>
	);
};

module.exports = App;

Наконец, давайте отобразим информацию в таблице. Нам нужно будет вложить друг в друга много боксов с некоторыми атрибутами. Наиболее распространенными атрибутами будут flex-direction и borderStyle. Поскольку мы используем React, мы все еще оперируем в реалиях JSX, и нам нужен родительский атрибут.

Внутри элемента Box под элементом TextBox мы и добавим нашу таблицу:

<Box flexDirection="column" width={80} borderStyle="single">
	<Box>
		<Box width="40%">
			<Text>Country Code</Text>
		</Box>

		<Box width="40%">
			<Text>Capital City</Text>
		</Box>

		<Box width="40%">
			<Text>Currency</Text>
		</Box>
	</Box>
	<Box>
		<Box width="40%">
			<Text>{phone}</Text>
		</Box>

		<Box width="40%">
			<Text>{capital}</Text>
		</Box>

		<Box width="40%">
			<Text>{currency}</Text>
		</Box>
	</Box>
</Box>

Давайте добавим баннер в наше приложение. Просто потому, что мы можем. Мы добавим его в корневой элемент Box поверх ввода текста.

<Box borderStyle="round" borderColor="green">
	<Text>Welcome to Country CLI</Text>
</Box>

Готово.

Теперь наш код выглядит так:

"use strict";
const React = require("react");
const { Text, Box } = require("ink");
const TextInput = require("ink-text-input").default;
const wcc = require("world-countries-capitals");

const App = () => {
	const [country, setCountry] = React.useState("");
	const [capital, setCapital] = React.useState("");
	const [currency, setCurrency] = React.useState("");
	const [phone, setPhone] = React.useState("");

	React.useEffect(() => {
		const getCountry = wcc.getCountryDetailsByName(country);
		setCapital(getCountry[0].capital);
		setCurrency(getCountry[0].currency);
		setPhone(getCountry[0].phone_code);
	});

	return (
		<Box flexDirection="column">
			<Box borderStyle="round" borderColor="green">
				<Text>Welcome to Country CLI</Text>
			</Box>
			<TextInput
				placeholder="Enter your country..."
				value={country}
				onChange={setCountry}
			/>
			<Box flexDirection="column" width={80} borderStyle="single">
				<Box>
					<Box width="40%">
						<Text>Country Code</Text>
					</Box>

					<Box width="40%">
						<Text>Capital City</Text>
					</Box>

					<Box width="40%">
						<Text>Currency</Text>
					</Box>
				</Box>
				<Box>
					<Box width="40%">
						<Text>{phone}</Text>
					</Box>

					<Box width="40%">
						<Text>{capital}</Text>
					</Box>

					<Box width="40%">
						<Text>{currency}</Text>
					</Box>
				</Box>
			</Box>
		</Box>
	);
};

module.exports = App;

Чтобы протестировать наше творение, запустим в нашем терминале node cli.

Он должен вернуть это:

Вы можете посмотреть гифку работы приложения по этой ссылке ссылке.

Примечание: запуск тестовой команды (npm run test) не сработает, потому что мы не написали никаких тестов. Ink по умолчанию использует для тестирования ava. Вы можете прочитать больше об ava в этой документации.

Заключение

Мы только что создали наш первый более-менее сложный интерфейс командной строки с использованием React, и вот несколько моментов, на которые следует обратить внимание. Ink содержит много элементов, позволяющих лучше контролировать пользовательский интерфейс командной строки. Он также поставляется с настраиваемыми хуками для управления данными, полученными с терминала. Хорошим примером является useInput, который слушает пользовательский ввод.

Создавать CLI-приложения еще никогда не было так просто. Получайте удовольствие, создавая более сложные и красивые приложения с интерфейсом командной строки. Чтобы ознакомиться с кодом, использованный в статье, вы можете перейти по этой ссылке.

Домашнее задание: на данный момент приложение вылетает, когда пользователь вводит несуществующую страну или совершает опечатку. Чтобы узнать больше о кастомных хуках React Ink и попрактиковаться в хуках React, попробуйте исправить эту ошибку, например, чтобы отображалась пустая таблица или сообщение об ошибке. Отправьте решение как PR в репо проекта.


Материал подготовлен в рамках специализации "Fullstack Developer".

Всех желающих приглашаем на бесплатное demo-занятие «Карточка товара». На открытом вебинаре создадим карточку товара. Возьмем за основу макет по продаже мебели, сделанный в figma, добавим html, css, анимации и js, если потребуется.
>> РЕГИСТРАЦИЯ




К сожалению, не доступен сервер mySQL