2018年6月6日
プログラミング
Reactでスマートスピーカーのコマンド発音サービスを作ろう(7)【作りながら覚えるReact】
はじめに
スマートスピーカーの発音サービスを作りながらReactを学んでいくシリーズ、第7弾です。
前回、フリーワード発音部分を実装していきました。
サービスはこちらです。
今回はカードのコマンド発音部分を実装していきます。
スピーカーの種類を切り替える
前回、こちらの図のheader.js部分を実装していきました。
今回はmain.js部分を実装していきます。
さて、main.jsですが、コンポーネントの中に更にコンポーネントを分割し(子コンポーネント化)、1つ1つのカードをspeaker_button.jsにしていきます。
スピーカー選択タブ
では、main.jsのスピーカー選択タブ部分を実装していきます。
ここは前回の記事と同じくラジオボタンを使っていきます。
以下のような流れでラジオボタンを作りましたね。
- constructorでStateにデフォルト値(alexa)を設定する
- render内のJSXでラジオボタンを作る。
1で設定したStateの値を使用し、alexaの場合にはalexaのラジオボタンのchecked
にtrueが入るように、googleの場合はgoogleのラジオボタンのchecked
にtrueが入るようにする - ラジオボタンが押されたら
onChange
で設定したイベントが発動する - イベント内でStateの更新が行われ、ラジオボタン部分が再レンダリングされる
- Stateの値が変わることで
checked
の判定が変わり、選択した部分がchecked
と判定されるようになる
では、実際にコードを見ていきましょう。
import React, { Component } from 'react';
export default class Main extends Component {
constructor(props) {
super(props);
this.state = {
speaker_type: "alexa",
};
this._toggleButton = this._toggleButton.bind(this);
}
_toggleButton(e) {
this.setState({speaker_type: e.target.value});
}
render() {
return (
<div className="main">
<div className="container">
<div className="tabs">
<input
type="radio"
value="alexa"
name="tab_item"
id="main_alexa"
checked={this.state.speaker_type === "alexa"}
onChange={this._toggleButton}
/>
<label className="tab_item" htmlFor="main_alexa">
<i className="fab fa-amazon"></i> Alexa
</label>
<input
type="radio"
value="google"
name="tab_item"
id="main_google"
checked={this.state.speaker_type === "google"}
onChange={this._toggleButton}
/>
<label className="tab_item" htmlFor="main_google">
<i className="fab fa-google"></i> Google Home
</label>
</div>
</div>
</div>
);
}
}
先程解説した流れの通りになっている為、難しい点はないと思います。
注意点としては、idをmain_alexa
、main_google
としている点です。
header.js内でラジオボタンにalexa
とgoogle
を使用している為、同じIDを使用すると重複してしまい上手く動かなくなります。
カードのコマンドを発音する
次にカード部分のspeaker_button.jsを実装していきます。
今回は1つ1つのカードをspeaker_button.jsの子コンポーネントに分ける為、以前実装したconstのファイルをmapしていく処理はmain.js側に書いていきます。
main.jsでカードを展開する
main.js
import SpeakerButton from './speaker_button';
import {Alexa} from '../const/alexa';
import {Google} from '../const/google';
まずSpeakerButton、AlexaとGoogleのデータをインポートしていきます。
<div className="tab_content" id="alexa_content">
<div className="row">
{Alexa.commands.map((command, i) => {
return (
<SpeakerButton
key={i}
title={command.title}
description={command.description}
pronunciation={command.pronunciation}
pre_commands={Alexa.pre_commands}
/>
);
})
}
</div>
</div>
<div className="tab_content" id="google_content">
<div className="row">
{Google.commands.map((command, i) => {
return (
<SpeakerButton
key={i}
title={command.title}
description={command.description}
pronunciation={command.pronunciation}
pre_commands={Google.pre_commands}
/>
);
})
}
</div>
</div>
{Alexa.commands.map((command, i) => {
こちらは以前の記事でもあった、コマンドの展開をmapメソッドで行っています。
command
は展開されたデータですが、その後ろのiはインデックス(番号)が入ります。
return (
<SpeakerButton
key={i}
title={command.title}
description={command.description}
pronunciation={command.pronunciation}
pre_commands={Alexa.pre_commands}
/>
);
こちらではSpeakerButtonコンポーネントを呼び出しています。
Propsとしてkey、title、description、pronunciation、pre_commandsを渡しています。
POINT!!
mapで展開したインデックスをkeyとしてコンポーネントになぜ渡しているのでしょうか。
ReactではVirtualDOMという仮想のDOMを保持し、実際に更新の対象となる部分(差分)だけDOM更新を行ってくれると以前の記事で解説しました。
この差分を検知する為、どのアイテムが差分が出たか特定する為にkeyが必要なのです。
keyが無いとどのアイテムか特定できず、展開したものを全てDOM更新してしまう為に効率が悪くなってしまいます。
ちなみにkeyを設定していなかった場合はブラウザのconsole上にワーニングが出てきます。
SpeakerButtonコンポーネント
では、speaker_button.jsを見ていきましょう
import React, { Component } from 'react';
export default class SpeakerButtons extends Component {
_onClick(pronunciation) {
let ssu = new SpeechSynthesisUtterance();
ssu.text = this.props.pre_commands + pronunciation;
ssu.lang = 'ja-JP';
speechSynthesis.speak(ssu);
}
render() {
return (
<div className="col-sm-6 mb-2 mt-2">
<button className="card" onClick={() => this._onClick(this.props.pronunciation)}>
<div className="card-body">
<h4 className="card-title text-left">
<i className="fas fa-play"></i>
{this.props.title}
</h4>
<p className="card-text text-left">
{this.props.description}
</p>
</div>
</button>
</div>
);
}
}
ここも今までのシリーズを見ていただければ、そんなに難しい点はないかと思います。
渡ってきたPropsの値を持ってカードを展開し、押された際に_onClick
を呼び出し、そこで発音をしてくれるという流れです。
main.js
改めて今回修正したmain.jsはこちらです。
import React, { Component } from 'react';
import SpeakerButton from './speaker_button';
import {Alexa} from '../const/alexa';
import {Google} from '../const/google';
export default class Main extends Component {
constructor(props) {
super(props);
this.state = {
speaker_type: "alexa",
};
this._toggleButton = this._toggleButton.bind(this);
}
_toggleButton(e) {
this.setState({speaker_type: e.target.value});
}
render() {
return (
<div className="main">
<div className="container">
<div className="tabs">
<input
type="radio"
value="alexa"
name="tab_item"
id="main_alexa"
checked={this.state.speaker_type === "alexa"}
onChange={this._toggleButton}
/>
<label className="tab_item" htmlFor="main_alexa">
<i className="fab fa-amazon"></i> Alexa
</label>
<input
type="radio"
value="google"
name="tab_item"
id="main_google"
checked={this.state.speaker_type === "google"}
onChange={this._toggleButton}
/>
<label className="tab_item" htmlFor="main_google">
<i className="fab fa-google"></i> Google Home
</label>
<div className="tab_content" id="alexa_content">
<div className="row">
{Alexa.commands.map((command, i) => {
return (
<SpeakerButton
title={command.title}
description={command.description}
pronunciation={command.pronunciation}
pre_commands={Alexa.pre_commands}
/>
);
})
}
</div>
</div>
<div className="tab_content" id="google_content">
<div className="row">
{Google.commands.map((command, i) => {
return (
<SpeakerButton
key={i}
title={command.title}
description={command.description}
pronunciation={command.pronunciation}
pre_commands={Google.pre_commands}
/>
);
})
}
</div>
</div>
</div>
</div>
</div>
);
}
}
まとめ
今回は子コンポーネント(カード部分)にmapで展開した値をPropsで渡していきました。
次回はGitHub Pagesへのデプロイやサービスを公開する為の細かい修正などを行っていきます。
Reactのお仕事に関するご相談
Bageleeの運営会社、palanではReactに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。
この記事は
参考になりましたか?
0
0
関連記事
2022年5月13日
Reactでオセロゲームを作る
2021年4月9日
Slack OAuth x React で Slack ユーザー認証を作ってみた!
2021年3月1日
Reactで使えるバリデーションライブラリを紹介!
2021年2月22日
Hasura Cloud × Auth0 × React でお手軽にTodoアプリを作ってみた!
2020年12月12日
Blitz.jsとTailwind CSSでメモ帳アプリの作成【第2弾】
2020年12月8日
コンポーネントを実装するときに意識すること
簡単に自分で作れるWebAR
「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。
palanARへpalanでは一緒に働く仲間を募集しています
正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。
運営メンバー
Eishi Saito 総務
SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。
sasakki デザイナー
アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。
やまかわたかし デザイナー
フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。
Sayaka Osanai デザイナー
Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。
はらた エンジニア
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
こぼり ともろう エンジニア
サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。
ささい エンジニア
フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。
Damien
WebAR/VRの企画・開発をやっています。森に住んでいます。
ゲスト bagelee
かっきー
まりな
suzuki
miyagi
ogawa
雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。
いわもと
デザイナーをしています。 好きな食べ物はラーメンです。
taishi kobari
フロントエンドの開発を主に担当してます。Blitz.js好きです。
kubota shogo
サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!
nishi tomoya
aihara
グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。
nagao
SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。
Kainuma
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
sugimoto
asama
ando
iwasawa ayane
oshimo
異業界からやってきたデザイナー。 palanARのUIをメインに担当してます。 これからたくさん吸収していきます!