20210113のReactに関する記事は7件です。

React setStateでstyle切り替え

全体

App.js
import React, {useState} from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
      <Func2/>
    </div>
  );
}

const Func2 = () => {
  const [bool,setBool] = useState(false);
  const status1 = {
    color:'blue'
  }

  const status2 = {
    color:'green'
  }

  return (
    <div>
      <h1 style={bool ? status1 : status2}>Hello</h1>
      <button onClick={()=> setBool(!bool)}>button</button>
    </div>
  );
}

export default App;

つくったやつ

const Func2 = () => {
  const [bool,setBool] = useState(false);
  const status1 = {
    color:'blue'
  }

  const status2 = {
    color:'green'
  }

  return (
    <div>
      <h1 style={bool ? status1 : status2}>Hello</h1>
      <button onClick={()=> setBool(!bool)}>button</button>
    </div>
  );

ステート変数boolが変更されると再レンダリングされる
ボタンを押すとboolが変更されて色が変わる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サンプルアプリで学ぶ React 開発 - Ignite UI for React で作るコロナウイルスのデータ可視化ダッシュボード

本記事ではインフラジスティックスで公開している Ignite UI for React のサンプルアプリケーションをご紹介します。この記事を通して Ignite UI for React の各コンポーネントを組み合わせてダッシュボードを作成する際の参考になれば幸いです。

React とは? Ignite UI for React とは?

React は、アプリケーション開発におけるフロントエンド構築のための JavaScript ライブラリです。世界的にも人気のライブラリとなっております。

Ignite UI for React は React での開発を効率化する UI 開発ツールです。大量のデータを扱う際に有用な高パフォーマンスのグリッドやチャート機能を実装することが出来ます。

コロナウイルスのデータ可視化ダッシュボードのサンプルアプリケーション

今回ご紹介する Ignite UI for React のサンプルアプリケーションは、世界各国における Covid-19 /コロナウイルスの感染拡大状況や時間の経過を表示するインタラクティブなWebサイトを表現したものです。

Image from Gyazo

サンプルの確認方法

こちらのページ から「サンプルを起動」ボタンをクリックしていただければ実際の挙動を確認することが出来ます。また、「サンプルをダウンロード」をクリックしていただければソースコードをダウンロードいただけます。

※サンプルソースをダウンロードするためには アカウント登録(無料) が必要です。

使用コントロールの紹介

このサンプルアプリケーションでは3つの Ignite UI for React コントロールを使用しています。

Image from Gyazo

  • 赤枠:Data Grid
  • 青枠:Data Chart
  • 緑枠:Geographic Map

Data Grid

Data Grid は表形式のコントロールで、コーディングや設定をほとんど行わずにデータをすばやくバインドして利用できます。今回のサンプルでは各国の感染者数/死亡者数のリストを対象者数順に表示するためにこのグリッドを利用していますが、フィルタリング、ソート、グリッド編集など豊富な機能を利用可能です。

Ignite UI for React データグリッドについての詳細は こちらのドキュメント をご参照ください。

Data Chart

Data Chart は様々なタイプのチャートと、チャートの表現に関する機能を提供します。本サンプルでは スプラインチャート散布ポイントチャート を組み合わせて実装しています。

Ignite UI for React データチャートについての詳細は こちらのドキュメント をご参照ください。

Geographic Map

Geographic Map は地図上に位置情報と連動したデータ表現を可能にするコンポーネントです。サンプルでは 散布図比例シリーズ を使用して感染拡大状況を可視化しています。

Ignite UI for React Geographic Mapについての詳細は こちらのドキュメント をご参照ください。

その他の実装機能の紹介

サンプルでは Ignite UI for React のコントロールを使用していない部分でも WEB アプリケーションにおいてよく使う機能が実装されています。React での開発において参考にしていただけると思います。

ローディング機能

Image from Gyazo

アプリケーションの表示準備が完了するまでのローディング表現を実装しています。

スライダー、タブ切り替え

Image from Gyazo

赤枠:タブクリックによるデータの切り替えを実装しています。

青枠:つまみを動かすような形で制御可能なスライダーコンポーネントを実装しています。

ツールチップ

Image from Gyazo

右上のアイコンにマウスオーバーすると出現するツールチップコンポーネントを実装しています。

テーマ切り替え

Image from Gyazo

パレットアイコンをクリックすると全体のテーマカラーを light/dark の2種類で切り替えて表示することが出来ます。

表示コンポーネントの任意選択

Image from Gyazo

任意で表示するコンポーネントを選択する機能を実装しています。

データの時間軸での切り替え

細かいタイムフレームで表示するデータをより新しい時間軸に更新することで、コロナウイルスによる状況がどのように変化しているのかアニメーションのような形で可視化する機能を実装しています。

公開データの取得

コロナウイルスの感染者数と死亡者数に関する最新の統計を ジョンホプキンス大学 から取得するためのデータサービスを実装しています。データを取得した後に localStorage にキャッシュとして保存し利用する処理も外部からデータを取得して利用する場合の参考になるかと思います。

まとめ

今回ご紹介したサンプルでは Ignite UI for React のコントロールを利用して、チャートやグリッドなど複雑な機能を持つコンポーネントを実装し、独自実装によるツールチップなどのシンプルな機能と組み合わせることでひとつのアプリケーションを実現しています。React 開発者の皆様にとっての助けや気付きがたくさん含まれたサンプルですので、ぜひソースコードをダウンロードしてお手元でご確認ください。

Ignite UI for React トライアル版を利用するには

Ignite UI for React はトライアル板での試用が可能です。
トライアル版を利用するためには こちらのページ よりアカウントの作成を行ってください。登録より30日間、弊社のテクニカルサポートをご利用いただくことが出来ますのでお気軽にお問い合わせください。

Ignite UI for React を使用した作業の開始に関しましては こちらのドキュメント をご参照ください。

また、製品をご購入をご検討のお客様は こちらのページ よりお気軽にお問い合わせください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactTransitionGroupによって使える様になる特殊なライフサイクルメソッド

ReactTransitionGroup

  • Reactでトランジションを実現したいときの低レベルコンポーネント
  • ReactCSSTransitionGroupよりも細かい設定を自前で実装したいときに使用

ReactCSSTransitionGroup

  • Reactでトランジションを実現したいときの高レベルコンポーネント

何がしたいのか

  • ルーティング先のコンポーネントで特殊ライフサイクルメソッド(後述)を呼び出せる様にしたい

Transition-groupに対して使えるライフサイクルメソッド

ReactTransitionGroupの説明(英語の方は公式)

en: https://ja.reactjs.org/docs/animation.html

ja: http://js.studio-kingdom.com/react/guides/animation

**<ReactTransitionGroup>タグの子コンポーネントで使用可能になる**

componentWillAppear()

componentWillAppear(callback)

This is called at the same time as componentDidMount() for components that are initially mounted in a TransitionGroup. It will block other animations from occurring until callback is called. It is only called on the initial render of a TransitionGroup.


componentDidAppear()

componentDidAppear()

This is called after the callback function that was passed to componentWillAppear is called.


componentWillEnter()

componentWillEnter(callback)

This is called at the same time as componentDidMount() for components added to an existing TransitionGroup. It will block other animations from occurring until callback is called. It will not be called on the initial render of a TransitionGroup.


componentDidEnter()

componentDidEnter()

This is called after the callback function that was passed to [componentWillEnter()](https://ja.reactjs.org/docs/animation.html#componentwillenter) is called.


componentWillLeave()

componentWillLeave(callback)

This is called when the child has been removed from the ReactTransitionGroup. Though the child has been removed, ReactTransitionGroup will keep it in the DOM until callback is called.


componentDidLeave()

componentDidLeave()

This is called when the willLeave callback is called (at the same time as componentWillUnmount()).

色々試した結果得た知見(暫定的)

  • ReactTransitionGroupは直下の子コンポーネントにのみ反応するっぽい
  • ルーティングで呼び出されるのはAppearのみ
  • 追加、削除の処理を明示的に行った場合はEnterLeaveが発火
  • componentWillAppearはデフォルトのcomponentDidMountと同時に実行するため、コールバックで処理を遅らせてもコンポーネントの表示を遅らせることはできない。

    //この時点で既にマウント完了している
    componentWillAppear(callback){
        console.log('top willappear')
        setTimeout(()=> {
            callback()
        }, 2000)
    }
    
    componentDidAppear(){
        console.log('top didappear')
    }
    
  • componentWillLeaveはコールバックが実行されるまでDOMをキープしてくれるので、コンポーネントの削除を遅らせる事が可能。

    //この時点ではまだDOMはキープされている
    componentWillLeave(callback){
        setTimeout(()=> {
            callback()
        }, 2000)
        console.log('top willleave')
    }
    
    //このタイミングでDOMが消える
    componentDidLeave() {
        console.log('top didleave')
    }
    

    ルーティングによるコンポーネントの切り替えの際にEnterとLeaveを発生させたい

    • EnterとLeaveはコンポーネントの「追加」「削除」時に発生する。
    • そもそもReactのルーティングはコンポーネントの「追加」「削除」にあたるのか?
    • EnterとAppearの違いは?(現状ルーティングで発生しているのがAppear)

    AppearとEnterとLeaveの違い

    上の英文から解釈

    Appear(現状ルーティングで使えている)

    • 初期マウントされているコンポーネントで呼び出される。
    • ルーティングの場合最初に全てのコンポーネントを用意しているためこちらになる?

    Enter(現状ルーティングで使えていない)

    • 既存の TransitionGroupに追加されたコンポーネントに対して、componentDidMount() と同時に呼び出されます。コールバックが呼ばれるまで、他のアニメーションが発生しないようにブロックします。TransitionGroup の初期レンダリング時には呼び出されません。
    • 既存のTransitionGroupに追加されたコンポーネントで呼び出される(新規追加)。

    Leave(現状ルーティングで使えていない)

    • これは、ReactTransitionGroupから子が削除されたときに呼び出されます。子が削除されても、ReactTransitionGroupはコールバックが呼ばれるまで子をDOMに保持します。
    • TransitionGroupからコンポーネントが削除されたときに呼び出される。
    • ルーティングの際は、削除せずコンポーネントを切り替えているだけなので実行されない?

    つまり、ルーティングはコンポーネントを入れ替えているだけで削除しているわけではないのでLeaveを呼び出せない?

    全てのルートを一つのReactTransitionGroupでラップして、かつその子孫のコンポーネント内でライフサイクルメソッドを使いたい

    • 直下にRouteがあるから反応しないのか?

    React Transition Group with Router

    https://reactcommunity.org/react-transition-group/with-react-router
    - ルートの遷移をアニメーション化したいと思うことがよくありますが、これは適度に使うことで楽しいUXになります。最初の方法はTransitionGroupですべてのルートをラップすることかもしれませんが、このアプローチはハックが必要で、RedirectのようなReact Routerのトリッキーなコンポーネントと一緒に使うと簡単にバラバラになります。各ルートにCSSTransitionを使い、それぞれのルートのin propを独自に管理する必要があります。
    - 主な課題は出口のトランジションです。React Routerは瞬時に新しいルートに切り替わるので、そこからトランジションするためには古いルートを長く維持しておく必要があります。幸いなことに、Routeのchildren propは関数も受け付けており、render propと混同してはいけません。レンダープロップとは異なり、children 関数はルートがマッチしているかどうかに関わらず実行されます。React Router は match オブジェクトを含むオブジェクトを渡しますが、これはルートがマッチしていれば存在し、そうでなければ null です。これにより、マッチの有無に応じてCSSTransitionのinプロップを管理できるようになりました。
    - 終了トランジションでは、ルートの内容が消えるまで残ってしまうため、スタイリング上の問題が発生する可能性があります。ルートがお互いのレイアウトに影響を与えないようにしてください。例えば、絶対配置や固定配置を使用することで、ドキュメントの流れからルートを削除することができます。
    - 注意:React トランジショングループを React Router で使用する場合、Switch コンポーネントは最初に一致する Route のみを実行するため、Switch コンポーネントの使用を避けるようにしてください。そうすると、終了するルートが現在の URL と一致しなくなり、子関数が実行されなくなるため、終了遷移を実現できなくなります。

    Transitionで全部をラップするのはやめとけみたいな事が書いてある。

    もしかしてReactTransitionGroup使わないほうがいい?

    https://agm1984.medium.com/how-to-manage-page-transition-animations-in-react-ba09c66655c6

    この記事ではTransitionGroupとTransitionでやっている

    この記事通りにやるとそれぞれフックは取れるけど、コンポーネントごとに独立して存在するので処理を止めるという事が難しい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでonClick={() =>がわからない

Reactでこういうコードがありました。

<Chip
 label={"ラベル"}
 onClick={() => handleClick("ラベル")}
/>

それで、これが解せなかった。

onClick={() =>

結論、今の僕の認識では、ユーザーがブラウザでChipをクリックすると、

() => handleClick("ラベル")

これで定義したhandleClickが実行されるという認識において、

MDNのアロー関数に書いてある仕様とは違うのかな?と思いました。

アロー関数は学びました

【JavaScript】アロー関数式を学ぶついでにthisも復習する話

このqiitaで書かれれる、

3行でまとめ
・JavaScriptのES6でアロー関数式という、今までとは違った関数の書き方が追加された
・アロー関数式は既存の関数式より文字数が短くなるだけではなく、宣言時のthisを束縛して不変のものにするという効果を持っている
・ほとんどの場面ではアロー関数式を使うほうがわかりやすくなるが、thisを束縛されて困る場面もあるので要注意!

というところも納得しました。

これを読んだ後でも、所謂アロー関数式の説明では、 onClick={() => handleClick("ラベル")}の動きはよく理解できなかった。

自分で試して、

var arguments = [1, 2, 3];
var arr = () => arguments[0];

 // 1
console.log( arr() )

だとコンソールに1が出てくるけど、

var arguments = [1, 2, 3];

 // () => arguments[0]
console.log( () => arguments[0] )

これだとコンソールに() => arguments[0]が出てくるということを見ても、

ブラウザからのクリック動作がまさに、onClick={() => handleClick("ラベル")}で言うところの、関数実行()になるものと判断しました。

そして、これはこういうもので、アロー関数のイロハとは違うと認識しています。

間違ってたら詳しく教えてください٩( 'ω' )و

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜJamstackでWEB制作の仕事を受けようと思ったのか

私自身もともとWordPressでweb制作の仕事を受けていた。プラグイン依存、CSS設計問題、サーバー代などのコスト面 etc。賛否両論あると思うけど、個人的にはしんどかった。

また、業務委託先でReactでサービス開発をずっとやっているのでJamstackでできたらと思っていた。なので、趣味でGatsbyやったりNetlifyCMSやったりして試していたりはしたが、趣味では良いけどクライアントに納品するものとしてと考えると難しいなという印象だった。

特に基本が英語なので、どうしても敷居が高くなってしまう。あとはリテラシーが高くないと無理かなとも率直に思った感じ。Jamstackを実際の案件でやりたいけど二の足を踏んでいたという状態だった。

そうこうしているうちにmicroCMSの存在を知って「あ、これなら案件でいける」と思い、実際の案件に投入することに決めた。

なぜJamstackでWEB制作の仕事を受けようと思ったのかは「microCMS」が比重を多く占めるのだけれど、Jamstack案件をここ数ヶ月で立て続けに3サイト作ったので実際の業務でやったプラクティス的なことをまとめる。

開発体験最高だよ

フロントエンドエンジニア御用達のprettier、eslint、TypeScriptが使えるの最高。

ReactやVueでサービス開発をしたことある人なら問題なくJamstackいける。これはかなりの利点だと思っていて、ある程度フロントをかじった人ならweb制作で受注をして稼ぐことが容易になったと思う。

ホスティングは迷うならNetlify一択

firebase, Amplifyなどがあるけど迷うならNetlifyで良い。

デプロイの設定が楽だし迷うこともない。また、お問合せが簡単に作れるのも魅力。

ちなみに、firebaseでお問合せを作ろうと思うとCloud Functionsに書かなければいかず有料になってしまう。Amplifyもそんな感じかな??やったことないからわからない。例えばDBに溜め込むとかしたいならfirebaseを選ぶけど、一般的な中小企業のコーポレートサイトならほぼほぼNetlifyで良い。またそういったコーポレートサイトであれば無料で済む。

ただ、泣きどころはTokyoリージョンがNetlifyにないため若干遅い。

これはゆくゆくは解決されるのではと思っているけど・・・。

webhookでSlackに通知は怠るな

デプロイの可否をSlackに通知できたり、お問合せがあったらなど各種連携が取りやすいので運用フェーズに入った時に絶対に必要になるから必ず設定すべし

NetlifyへのデプロイをSlackに通知する | Unselfish Meme

【Netlify】Forms機能を利用して問い合わせフォームを作成する - Qiita

2021/01/14 追記
どうやら以前の価格改定により無料枠ではSlackやメールなどの通知ができなくなっている。ちなみに、無料枠である私のアカウントはできる。もしかしたら改訂前にアカウント持っていた場合は通知ができるのかもしれない。

アカウント持っている方はホスティングしたサイトを選択 > Site settings > Build & Deploy > ページ下部のDeploy notifications > Add notification で確認した方が良い。
notificationができるのであれば下記のように選べるようになっている。
image.png

もし今後アカウントを取得してNetlifyで通知したい場合は「pro」にしなければならない。

Netlify Pricing and Plans

microCMSを選ぶべし

microCMSのまわし者みたいだけど、まじでmicroCMSが今のところ一番良い

日本語だし、UIも日本人にわかりやすく受け入れられやすい

microCMS | APIベースの日本製ヘッドレスCMS

プレビュー機能は絶対入れとけ

どのクライアントにも言われたけど「記事作成したんだけどプレビューできないんですか?」

そんなに難しいことをせずに実装できるので絶対に入れるべし

[https://your-service-name.netlify.app/draft/?id={CONTENT_ID}&draftKey={DRAFT_KEY}](https://your-service-name.netlify.app/draft/?id=%7BCONTENT_ID%7D&draftKey=%7BDRAFT_KEY%7D) を叩くプレビュー用のページがあれば良いだけ。

microCMSとNuxtでプレビュー画面を作成する

記事更新などでデプロイが走るようにするのを忘れるべからず

APIのエンドポイントごとの設定なので注意すべし

これやっておかないと「記事更新したのにサイトに反映されてないんですけど!」と連絡来てしまう。

Webhook機能がパワーアップしました

lighthouseで満点狙うべし

私はNextJSでしか開発をしていないが多少の知識があれば満点ないしかなりの高得点は簡単に出る
image.png
基本的にはlighthouseで採点 > 注意されたとこを直すフローで良いが、service-workerに関してはプラグインを入れればすぐに解決できてオフラインでも閲覧できるようになる。

hanford/next-offline: make your Next.js application work offline using service workers via Google's workbox

あとは、やはり画像は重いことが多いので、画像サイズには注意する必要がある。

画面いっぱいまで画像を広げたい時などは、簡単に済ますなら画質をある程度落としてしまうし、もしくはsrcset属性を使って最適化するように心がける。また、TinyPNGなどを使って圧縮もするようにする。

Jamstackの泣きどころ

これはstg/prdで分けて開発をしにくい点にあると思う。プレビュー機能の実装である程度は補えるとしても、開発者としてはstgは欲しいなと思う。
graphcmsではenvironment-endpointsとして masterdev でエンドポイントのだしわけが可能なよう。が、残念ながら思いっきり英語なので実務では難しい。(microCMSさんお願い!!!)

https://graphcms.com/docs/environments#environment-endpoints

個人的に今後やっていきたいこと

今後は案件をもっと獲得できるようにするつもり。また、それに合わせて外注も増やさざるをえないかなと思っていて、外注しても一定の品質が保てるようにしたいので開発フローの整備等をしていきたい。

  • storybookを入れて別ドメインにデプロイして共有するようにしたい
  • reviewdog の導入(TSのチェックをPR作成時などでチェックするようにしたい)
  • ガイドラインなどのドキュメント作成
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React/Route 指定ページのみキーボード操作

次のようにRouteを設定している状態で、指定のページ(main)のみで、window.addEventListener('onkeydown'~からキーボード操作を定義すると、指定のページ以外(sub)でもキーボード操作を受け付けてしまい、キーボード操作時の内容によってはエラーになります。

  render() {
    return (
      <HashRouter>
        <div className="content-area">
          <Route exact path='/main' render={() => <Main /> } />
          <Route exact path='/sub' render={() => <Sub /> } />
        </div>
      </HashRouter>
    );
  }

指定コンポーネント内のdiv要素にonKyeDownイベント定義すれば、そのページ内のみキーボード操作を受け付けますが、そのdiv要素をクリックする必要があり、若干ユーザビリティが気になります。

  render() {
    return (
        <div onkeyDown={() => this.KeyDownAction()}>
        ~~
        </div>
    );
  }

対応

指定のページへ遷移する際(マウント)にcomponentDidMountメソッドが呼び出され、指定ページから離れる際(アンマウント)にcomponentWillUnMountメソッドが呼び出されます。
よって、次のように実装することで、指定ページのみで指定のキーボード操作関数を受け付ける事ができます。

class Main extends Component {

  constructor(prop) {
    super(prop);

    this.bindKeyDownAction = this.KeyDownAction.bind(this);
  }

  componentDidMount() {
    window.addEventListener(
      "keydown",
      this.bindKeyDownAction,
      false
    );
  }

  componentWillUnmount() {
    window.removeEventListener(
       "keydown",
       this.bindKeyDownAction,
       false
    );
  }

コンポーネント内のメンバ変数にアクセスする場合は.bind(this)が必要になります。また、.bind(this)した関数を直接addEventListenerに渡すと、removeEventListenerする際の関数として指定する事ができなくなるため、例では、別の変数(this.bindKeyDownAction)に設定し、その変数をaddEventListenerに渡しています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文章を?????な帰国子女っぽくしてくれるクソアプリを作った

文章をかっこよくしたいことってありますよね?
かっこいい文章と言えば、アメリカ帰りの帰国子女やイケてる外資系コンサル社員がニューヨークの風をまとわせて書く、ルー大柴のような英語まじりの文です。
ついでに英文のスタイルも変えて?????な表記にできれば、平凡な文字列も鮮やかに生まれ変わるでしょう。

しかし、単語を翻訳してスタイルの変更を適用するのはなかなか手間です。そこで、一連の変換を自動化する ??????? ??? を作ってみました。

https://notra.herokuapp.com/
CEFA7764-71D4-4809-A6A7-8E78FA0EE2F4.jpeg

概要

おおまかにはこのアプリは以下の流れで動いています。

  1. 入力文を受け取る
  2. 形態素解析を適用
  3. 名詞を Google Cloud の Translation API で翻訳
  4. 英語部分のUnicodeをいろんなパターンに変換して表示

フロントエンド

フレームワーク

WebフレームワークはReactを使いました。その上で、UIフレームワークにAnt Designを使いました。Ant Designはアリババが提供するオープンソースのReact用UIフレームワークで、以下のように書くだけでコンポーネントのUIをいい感じにしてくれます。

import { Tabs, Typography } from "antd";

import { TabContent } from "../../types";

const { Paragraph } = Typography;

const { TabPane } = Tabs;

type StyleTabsProps = {
  options: TabContent[];
  onChange: (key: string) => void;
};

export const StyleTabs = (props: StyleTabsProps) => {
  const tabOptions = props.options.map((op) => (
    <TabPane tab={op.tabName} key={op.key}>
      <Paragraph className="Paragraph" copyable>
        {op.value}
      </Paragraph>
    </TabPane>
  ));
  return <Tabs onChange={props.onChange}>{tabOptions}</Tabs>;
};

Unicode変換

Fancy -> ?????のような変換を行うために、Fancifyというパッケージを使わせていただきました。

以下は実装の一部です。スタイルによっては小文字しかなかったり大文字しかなかったりするため、それに対応してアルファベットの大小を変えています。

const SentenceTransformer = (sentence: str): TabContent[] => {
  const sets = [
    { style: "circled", letter: "both" },
    { style: "negative circled", letter: "upper" },
    { style: "fullwidth", letter: "both" },
    { style: "math bold", letter: "both" },
    { style: "math bold fraktur", letter: "both" },
    { style: "math bold italic", letter: "both" },
    { style: "math bold script", letter: "both" },
    { style: "math double struck", letter: "lower" },
    { style: "math mono", letter: "both" },
    { style: "math sans", letter: "both" },
    { style: "math sans bold", letter: "both" },
    { style: "math sans italic", letter: "both" },
    { style: "math sans bold italic", letter: "both" },
    { style: "parenthesized", letter: "both" },
    { style: "regional indicator", letter: "upper" },
    { style: "squared", letter: "upper" },
    { style: "negative squared", letter: "upper" },
  ] as const;

  const transformedSentences: TabContent[] = sets.map((x, index) => {
    let tabName = "Tab";
    let casedSentence = sentence;
    if (x.letter === "lower") {
      tabName = tabName.toLowerCase();
      casedSentence = sentence.toLowerCase();
    } else if (x.letter === "upper") {
      tabName = tabName.toUpperCase();
      casedSentence = sentence.toUpperCase();
    }
    const key = index + 1;
    return {
      tabName: fancify({ input: tabName + key, set: x.style }),
      value: fancify({
        input: casedSentence,
        set: x.style,
      }),
      key: key,
    };
  });

  return transformedSentences;
};

バックエンド

バックエンドのAPIサーバーにはExpressを使いました。Expressにはexpress-generatorというスケルトン生成ツールがありますが、これが生成してくれるのはJavaScriptのファイル群です。今回はTypeScriptを使いたかったので似たようなものがないか探したところ、express-generator-typescriptというまさに求めていたものがあったので使わせていただきました。こちらは本家よりもさらにいろいろ生成してくれます(今回はクソアプリを作るだけなのでやや過剰感もありましたが……)。

形態素解析

形態素解析はkuromoji.jsのラッパーであるkuromojinを使わせていただきました。
TinySegmenterは分かち書きソフトウェアであり形態素解析による品詞推定はできないので、名詞を見つける必要がある今回の用途には適しませんでした)。

以下は形態素解析で名詞を取り出す部分のスニペットです。

tokenize(text).then((tokenized) => {
  const nouns = tokenized
    .filter((token) => token.pos === "名詞")
    .map((x) => x.surface_form);
}

翻訳

名詞だけ変換できればいいので和英辞書を使うことも考えたのですが、あまりいい辞書がネット上に見つからなかったことと、辞書にない語の扱いをローマ字変換するだけよりは機械に無理やり変換させたほうがおもしろそうだな、と思い機械翻訳を使いました。

最初は月に100万文字まで無料のWatson Language Translatorを使おうかと考えていたのですが、機械翻訳APIの選定時はまだバックエンドを用意せずフロントエンドだけで頑張ろうとしていた(最終的に形態素解析の辞書の重さがネックになりバックエンドを用意することにしました)ため、CORSに対応していないWatson Language Translatorは使えずGoogle CloudのTranslation APIを使うことにしました。こちらは月50万文字まで無料ですが、まあたぶん大丈夫でしょう。

インフラやリポジトリ構成

URLを見ればわかりますがデプロイ先にはherokuを使いました。どうせなら使ったことのないサービスを使ってみたかったのですが、サービスをDockerコンテナ化していて、コンテナのデプロイを無料でさっとできそうなのがherokuくらいだったので今回もお世話になりました。

プロジェクト管理はlernaを使ってモノレポにしてみました。以下のディレクトリ構造のclientディレクトリにReactのコード、serverにexpressのコード、sharedに共通の型ファイルなどが置いてあります。

.
├── Dockerfile
├── README.md
├── heroku.yml
├── lerna.json
├── package-lock.json
├── package.json
└── packages
    ├── client
    ├── server
    └── shared

おわりに

年末年始あまりにも暇だったので始めたのですが、結果的にいろんな技術やツールに触れて楽しかったです。ぜひ遊んでみてください。

https://notra.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む