- 投稿日:2019-07-16T22:54:37+09:00
export defaultってなんだろう
問題
突然ですが、ここにこんなSampleコンポーネントがあります。(この記事はちょっとしたReact要素を含んでいますがReactを知らなくても多分わかります。)
sample.tsxexport const Sample: FC<Props> = () => { return(<div>sample</div>); }これは、いかにもこんな感じでimportして使えそうです。
sampleContainer.tsximport { Sample } from './sample.tsx'; import { connect } from 'react-redux'; const mapStateToProps = .... ; // 割愛 export default connect(mapStateToProps)(Sample);さて、ここでexport defaultされているこの子はいったいどのようにimportしたら他の場所で使えるのでしょうか。
答え
答えは以下のようになります。
routing.tsximport SampleContainer from './sampleContainer.tsx'; const participantRoutes: Routes = [ { path: "/activity", component: SampleContainer } ];ファイル名と対応しているのかな?と一見思うかもしれませんが、これは唯一の回答ではありません。これでもいいのです。
routing.tsximport SC from './sampleContainer.tsx'; const participantRoutes: Routes = [ { path: "/activity", component: SC } ];と言うか、なんでもいいのです。(Syntax上はですが)
なんででしょうか。考えてみたらとても自明なことだし納得のいくことなのですが、ReactでReduxするまでこれを意識しなければいけない場面がなかったので全く気づかず、不思議に思っていました。
解説
ポイントは
export default
です。これは文字通り、そのファイルのデフォルトとして後ろに続くものをexportするよ、という意味になります。
export default
されたものを使いたい時、importする側は任意の名前をつけることができます。それは、これをデフォルトにするよ、と宣言しているからです。
これは自然に理解できると思いますが、デフォルトを複数指定することはできません。なので、1ファイル内にさっきのconnectのようなexport default
宣言を複数書くとエラーが出ます。
なるほど、だから名前を好きにつけてもちゃんとconnectが使えたのですね詳しい説明: MDN Export
- 投稿日:2019-07-16T17:37:54+09:00
React.jsでPersoniumアプリを開発する最小サンプル
はじめに
Personiumを勉強するにあたって何度かアプリを作っているのですが、「お作法」が多く、ドキュメントのいろんなページを参照しなければならないので最初の一歩を踏み出す前に力尽きてしまうことが多々あります。
自分自身の力尽きた経験を元に、数ファイル修正するだけでアプリが作れるサンプルを用意してみました。
実際は自分用に作ったものです今回の内容はすべて以下のGitHubリポジトリにおいてあります。
→personium-blank-app「わかりづらい!」「こうした方がよい!」などのご意見はコメントまたはIssueにてお願いいたします!
作るもの
今回はReact.jsを使用した、「Personium」アプリを実装します。
Personiumアプリ、とは?
参考:「Personium アプリ」と「Personium を使ったアプリ」
要は、Personiumを使用するにあたって、
- ログインはPersonium事業者が提供するホームアプリから行う
- アプリはAppマーケットから各自がインストールする
などといったPersoniumのルールに従ってアプリを作る、いうのが「Personiumアプリ」です。
推奨されているのは「Personiumアプリ」ですが、OAuth2を使用したログインなどでPersoniumにアクセスするアプリも実装可能です。(後者は「Personiumを使ったアプリ」と呼ばれます。)
手順
上記リポジトリから、ソースコードをクローンします。
設定ファイルの更新
ビルド・デプロイ設定
config.example.js
→config.js
へリネームし、修正します。module.exports = { personium: { CELL_NAME: '<CELL_NAME>', // アプリセル名 CELL_FQDN: '<CELL_FQDN>', // アプリセル名.ユニットFQDN CELL_ADMIN: '<ADMIN_USERNAME>', // rootユーザーID CELL_ADMIN_PASS: '<ADMIN_PASSWORD>', // rootユーザーパスワード // 中略 } };アプリセル設定
src/assets/launch.example.json
→src/assets/launch.json
へリネームし、修正します。{ "personal": { "web": "<CELL_FQDN>/__/front/app", "android": "***:", "ios": "***;" } }
<CELL_FQDN>
にはアプリセルのFQDNを入力します。ユーザーセル内アプリ設定①
src/bar/00_meta/00_manifest.example.json
→src/bar/00_meta/00_manifest.json
へリネームし、修正します。{ "bar_version": "2", "box_version": "1", "default_path": "<DEFAULT_BOX_NAME>", "schema": "<APP_CELL_FQDN>" }
<DEFAULT_BOX_NAME>
にはユーザーセル内で使用するbox名を入力します。<APP_CELL_FQDN>
にはアプリセルのFQDNを入力します。ユーザーセル内アプリ設定②
src/bar/00_meta/90_rootprops.example.xml
→src/bar/00_meta/90_rootprops.xml
へリネームします。ビルド
barファイルのビルド
アプリ配信時に配布するbarファイルをビルドします。
npm run build-bar
dist/{アプリセル名}.bar
というファイルが生成されていれば成功です。これをユーザーのセルでインストールします。
今回はAppマーケットからのインストールは利用できないので、ホームアプリのBOXインストール機能を使用します。アプリのビルド
アプリのビルドは下記コマンドで実行します。
npm run build-appビルドしたものは
build
フォルダ配下に配置されます。デプロイ
ビルド生成物のアップロード
下記コマンドを実行することで先程のコマンドでビルドしたファイルをアップロードします。
npm run deployACLの設定
ACLの設定は手動で行います。アプリセルのセルマネージャを開き、以下を実行します。
/__/front
の all に exec を付与します。- Service
/__/front
内のスクリプトlaunghSPA.js
に ServicePathapp
という名前を付けます。![]()
/__/public
の all に read を付与します。アプリ情報の開示設定
下記4ファイルの all に read を付与します。
- launch.json
- profile.json
- relations.json
- roles.json
実行
barをインストールしたユーザーのホームアプリからアイコンをクリックするとアプリが起動します。
このとき、実行されるのは、
src/app/frontend/index.js
に実装されたコードです。import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <h1>Hello React!</h1>, document.getElementById('root') );本コードを修正することでReact.jsを使用した、SPAアプリケーションを開発することができます。
おわりに
以上、Reactを使用してPersoniumアプリを開発するサンプルでした。
個人的に一番欲しかった機能がgulpにも記載してあります、スクリプトによる資材のアップロードです。
これを使用することで、かなりアップロードのストレスを軽減できると思います。なお、一部ACL設定を手動でやっていますが、ファイルの上書きではACLは変更されませんので、
2度目以降のnpm run deploy
コマンドでは必要ありません。次回はFluxを使用した本格的なSPAに挑戦するQiitaを書くかもしれません。
- 投稿日:2019-07-16T15:09:15+09:00
Reactメモ
はじめに
Reactよくわからない。
React使う
色々調べてみたけどベースとなる経験が圧倒的に無いのでReactの利点というものがよく分からなかった。
複数サンプルを使ってみて、どれもフォーム入力したら同時に描画するものだったのでそれが特徴なのかなって思った。
電卓を作ってみた。(書かない)headタグ内<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>書いておく。
出力
ReactDOM.render
を使って表示する。
下の例だとHello World!
と表示される。bodyタグ内<div id="test"></div> <script type="text/babel"> const name = "world"; class Hello extends React.Component{ render() { return ( <p>hello {name}!</p> ); } } ReactDOM.render( <Hello />, document.getElementById("test") ) </script>
<div>タグ
に<p>タグ
が埋め込まれている。
const name = "world";
JavaScriptの変数。
class Hello extends React.Component{~
Reactのコンポーネントを生成している。クラス名は先頭大文字の必要がある。
render() {~
React要素を返している。
<p>hello {name}!</p>
JSX(よく分からない)。{}の中にJavaScriptの文を書ける。
ReactDOM.render(~
React要素をレンダリングする。第一引数はReact要素、第二引数は埋め込み先の要素。こうも書ける
Hello
と出る。
World!bodyタグ内<div id="test"></div> <script type="text/babel"> class Hello extends React.Component{ render() { return ( <div> <p>Hello </p> <World /> </div> ); } } class World extends React.Component{ render() { return ( <p>World!</p> ); } } ReactDOM.render( <Hello />, document.getElementById("test") ) </script>
<div>
要素は一つしか返せないので複数返したいときは一つにまとめる。
<p>Hello </p>
<World />
</div>
<World />
別コンポーネントを呼べる。state
コンポーネントにstateを追加する。
ボタンを押すと押した数が追加され、表示される。bodyタグ内<div id="testState"></div> <script type="text/babel"> class TestState extends React.Component{ constructor(props) { super(props); this.state = { disp: ''}; this.onClickBtn = this.onClickBtn.bind(this); } onClickBtn(e) { const str = this.state.disp + e.target.value; this.setState({disp: str}); } render() { const buttons = [1, 2, 3]; return ( <div> <Display disp={this.state.disp} /> {buttons.map(button => { return ( <Button key={button} onClickBtn={this.onClickBtn} button={button}/> ) })} </div> ); } }; class Display extends React.Component{ render() { return ( <p>{this.props.disp}</p> ); } } class Button extends React.Component{ render() { const val = this.props.button; return ( <button type="button" className="btn" value={val} onClick={this.props.onClickBtn}>{val}</button> ); } } ReactDOM.render( <TestState />, document.getElementById("testState") ) </script>
constructor(props) {~
stateを作る。setState()で値を更新すると再レンダリングされる。自作関数内でthis
を使う場合bindしておく。参照。
<Display disp={this.state.disp} />
Displayコンポーネントにstateのdispをdispという名前で渡している。
<Button key={button} onClickBtn={this.onClickBtn} button={button}/>
map関数で繰り返されている。key="ユニークな値"を設定しないとエラーがでる。
<p>{this.props.disp}</p>
渡された値はthis.props.名前で受け取る。
- 投稿日:2019-07-16T14:50:18+09:00
Redux stateにimmutable.jsを使う
immutable.jsをRedux stateに使ってみました。本記事は簡単な使い方の紹介になります。
Immutable collections for JavaScriptImmutabilityはアプリのパフォーマンスに貢献します。加えてプログラミングやデバッグをよりシンプルにしてくれます。
ImmutabilityとReduxの関連は、以下の記事に詳しく書いてあります。
Redux FAQ: Immutable DataTodoアプリ
React ReduxにあるTodoアプリにimmutable.jsを導入してみました。
環境は簡単に構築できます。
yarn create react-app todo-immutable cd todo-immutable yarn add redux react-redux classnames yarn add immutable rm -rf src4個のファイルを修正しました。必要に応じて、オリジナルと比べてみてください。ざっくり言って、コードが短くなっています。
state.todosをMapのListとして利用していきます。
src/redux/reducers/todos.jsimport { ADD_TODO, TOGGLE_TODO } from "../actionTypes"; // ListとMapを使います import { List, Map } from "immutable"; export default function(state = List(), action) { switch (action.type) { case ADD_TODO: { const { id, content } = action.payload; return state.push( // ListにMapを追加 Map({ id, content, completed: false }) ); } case TOGGLE_TODO: { const { id } = action.payload; return state.map(todo => { // Listのmapで処理 if (todo.get("id") === id) { return todo.update("completed", v => !v); // Mapのupdate } return todo; }); } default: return state; } }ListのfilterやfilterNotを使ってセレクタ関数を定義します。Mapのtodoにはtodo.get()でアクセスします。
src/redux/selectors.jsimport { VISIBILITY_FILTERS } from "../constants"; export const getTodosByVisibilityFilter = (store, visibilityFilter) => { switch (visibilityFilter) { case VISIBILITY_FILTERS.COMPLETED: // Listのfilter関数。Mapのget関数。 return store.todos.filter(todo => todo.get("completed")); case VISIBILITY_FILTERS.INCOMPLETE: // ListのfilterNot関数。Mapのget関数。 return store.todos.filterNot(todo => todo.get("completed")); case VISIBILITY_FILTERS.ALL: default: return store.todos; } };toObject()でMapをJSON Objectに変換します。
src/components/Todo.jsimport React from "react"; import { connect } from "react-redux"; import cx from "classnames"; import { toggleTodo } from "../redux/actions"; const Todo = ({ todo, toggleTodo }) => { // toObject()でMapをJSON Objectに変換。 const { id, content, completed } = this.props.todo.toObject(); return ( <li className="todo-item" onClick={() => toggleTodo(id)}> {todo && completed ? "〇" : "✖"}{" "} <span className={cx( "todo-item__text", todo && completed && "todo-item__text--completed" )} > {content} </span> </li> ); } // export default Todo; export default connect( null, { toggleTodo } // mapDispatchToProps を Object として定義 )(Todo);Mapのget関数でidを取得します。
src/components/TodoList.jsimport React from "react"; import { connect } from "react-redux"; import Todo from "./Todo"; import { getTodosByVisibilityFilter } from "../redux/selectors"; import { VISIBILITY_FILTERS } from "../constants"; const TodoList = ({ todos }) => ( <ul className="todo-list"> {todos && todos.size ? todos.map((todo, index) => { // Mapのget関数でidを取得 return <Todo key={`todo-${todo.get("id")}`} todo={todo} />; }) : "No todos, yay!"} </ul> ); const mapStateToProps = state => { const { visibilityFilter } = state; const todos = getTodosByVisibilityFilter(state, visibilityFilter); return { todos }; }; // export default TodoList; export default connect(mapStateToProps)(TodoList);今回は以上です。
- 投稿日:2019-07-16T13:49:50+09:00
【Firebase Hosting】SPAなサイトでPage Not foundとでた
React.js(SPA)で構築したサイトをFirebase Hostingを用いてデプロイすると、
以下のように、404 Page Not Found
と表示された。firebase.jsonでのHostingの設定に問題があったらしい。
変更前
firebase.json{ "storage": { "rules": "storage.rules" }, "hosting": { "public": "build", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] } }変更後
firebase.json{ "storage": { "rules": "storage.rules" }, "hosting": { "rewrites": [ { "source": "**", "destination": "/index.html" } ], "public": "build", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] } }これを追加した。
"rewrites": [ { "source": "**", "destination": "/index.html" } ],参照: 公式ドキュメント
- 投稿日:2019-07-16T10:54:31+09:00
reactのソースをビルドして公開する
- 投稿日:2019-07-16T01:07:08+09:00