react-blessedを使ってみる


概要

react-blessedは、Reactのblessed.js向けのrendererです。

blessed.jsのウィジェットを、Reactのコンポーネントを使用して描画することができます。

ターミナル上で動作するアプリを作成する際に便利そうだったため、使ってみました。


動作環境


  • Ubuntu 18.04

  • Node.js 8.11.4

  • blessed 0.18.1

  • react-blessed 0.3.0

  • Babel 7


インストール

$ yarn add blessed react react-blessed

$ yarn add @babel/cli @babel/core @babel/plugin-transform-react-jsx @babel/plugin-transform-modules-commonjs @babel/plugin-proposal-class-properties --dev


Hello world


ビルド設定

.babelrcに必要な設定を記述します


.babelrc

{

"plugins": [
["@babel/plugin-transform-react-jsx"],
["@babel/plugin-transform-modules-commonjs"],
["@babel/plugin-proposal-class-properties"]
]
}


ソースコード


src/index.js

import React, { Component } from 'react';

import blessed from 'blessed';
import { render } from 'react-blessed';

class App extends Component {
render() {
// blessed.jsのBoxを描画する
return (
<box
width='100%'
height='10%'
border={{ type: 'line' }}
style={{ border: { fg: '#888' } }}>
Hello, world
</box>
);
}
}

const screen = blessed.screen({
autopadding: true,
smartCSR: true,
title: 'Test',
fullUnicode: true,
});

// Escapeキーで終了
screen.key(['escape'], (ch, key) => {
process.exit(0);
});

render(<App></App>, screen);



ビルド

ビルドする際は、package.jsonにスクリプトを記述しておくとよいと思います。

ウォッチしたい場合は、--watchをつけます。


package.json

  "scripts": {

"build": "babel src --out-dir lib"
}

$ yarn run build


実行

$ node ./lib/index.js

Hello, worldが画面に出力されると思います。

Escapeキーを押すと、終了します。


tags

blessed.jsのタグを利用する際は、要素に対してtags属性を指定します。

その際に、タグを含むテキストを子として指定するのではなく、

content属性に指定する必要があります。

class App extends Component {

render() {
// <text tags>{'{green-fg}{bold}Hello, world{/bold}{/green-fg}'</text>
// ↑だと、タグが解釈されない
return (
<text
tags
content={'{green-fg}{bold}Hello, world{/bold}{/green-fg}'}>
</text>
);
}
}


refs

react-dom同様、ref属性が使えます。

class App extends Component {

constructor(props) {
super(props);

this.state = {
selectedUser: ''
};
}

componentDidMount() {
// コンポーネントに対する参照
this.refs.userList.focus();
}

onUserSelected(item) {
this.setState({ selectedUser: item.content });
}

render() {
return (
<box height='40%' border={{ type: 'line' }}>
<box height='10%'>{'selected: ' + this.state.selectedUser}</box>
<box top='10%' height='20%'>
<UserList
ref='userList'
onUserSelected={this.onUserSelected.bind(this)}
users={['hoge', 'fuga', 'piyo']}>
</UserList>
</box>
</box>
);
}
}

class UserList extends Component {
focus() {
// blessed.jsのNodeに対する参照
this.refs.list.focus();
}

render() {
const { users, onUserSelected } = this.props;

return (
<list
ref='list'
onSelect={onUserSelected}
items={['hoge', 'fuga', 'piyo']}
style={{
selected: {
bg: '#373b41',
fg: '#c5c8c6'
}
}}
vi
keys>
</list>
);
}
}

ref属性を指定した要素は、コンポーネントのrefsプロパティから参照することができます。


prop-types

import PropTypes from 'prop-types';

class UserList extends Component {
static propTypes = {
users: PropTypes.func.isRequired,
onUserSelected: PropTypes.func.isRequired
};

...
}


neo-blessed

blessed.js自体は、現在、あまりメンテナンスされていないようです。

そのため、neo-blessedというfork版が存在します。

react-blessedからneo-blessedを利用する際は、以下の設定を記述します。

import blessed from 'neo-blessed';

import {createBlessedRenderer} from 'react-blessed';

const render = createBlessedRenderer(blessed);

...

render(<App></App>, screen);


ユニットテスト

Enzymeやreact-testing-library等のライブラリは、現在のところ対応していません。

react-test-rendererを使うとよいのだろうか?

import test from 'tape';

import ShallowRenderer from 'react-test-renderer/shallow';
import ChatMessage from './ChatMessage';

test('/components/ChatMessage', assert => {
const renderer = new ShallowRenderer();

const output = renderer.render(
<ChatMessage
top={0}
height={20}
width='100%'
content='{bold}hoge{/bold}'>
</ChatMessage>
);

assert.equal(
output.props.content,
'{bold}hoge{/bold}',
'should have content'
);
assert.end();
});


参考