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

export defaultってなんだろう

問題

突然ですが、ここにこんなSampleコンポーネントがあります。(この記事はちょっとしたReact要素を含んでいますがReactを知らなくても多分わかります。)

sample.tsx
export const Sample: FC<Props> = () => {
    return(<div>sample</div>);
}

これは、いかにもこんな感じでimportして使えそうです。

sampleContainer.tsx
import { Sample } from './sample.tsx';
import { connect } from 'react-redux';

const mapStateToProps = .... ; // 割愛

export default connect(mapStateToProps)(Sample);

さて、ここでexport defaultされているこの子はいったいどのようにimportしたら他の場所で使えるのでしょうか。

答え

答えは以下のようになります。

routing.tsx
import SampleContainer from './sampleContainer.tsx';

const participantRoutes: Routes = [
  {
    path: "/activity",
    component: SampleContainer
  }
];

ファイル名と対応しているのかな?と一見思うかもしれませんが、これは唯一の回答ではありません。これでもいいのです。

routing.tsx
import 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が使えたのですね:sunny:

詳しい説明: MDN Export

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

React.jsでPersoniumアプリを開発する最小サンプル

はじめに

Personiumを勉強するにあたって何度かアプリを作っているのですが、「お作法」が多く、ドキュメントのいろんなページを参照しなければならないので最初の一歩を踏み出す前に力尽きてしまうことが多々あります。

自分自身の力尽きた経験を元に、数ファイル修正するだけでアプリが作れるサンプルを用意してみました。
実際は自分用に作ったものです

今回の内容はすべて以下のGitHubリポジトリにおいてあります。
personium-blank-app

「わかりづらい!」「こうした方がよい!」などのご意見はコメントまたはIssueにてお願いいたします!

作るもの

今回はReact.jsを使用した、「Personium」アプリを実装します。

Personiumアプリ、とは?

参考:「Personium アプリ」と「Personium を使ったアプリ」

要は、Personiumを使用するにあたって、

  • ログインはPersonium事業者が提供するホームアプリから行う
  • アプリはAppマーケットから各自がインストールする

などといったPersoniumのルールに従ってアプリを作る、いうのが「Personiumアプリ」です。

推奨されているのは「Personiumアプリ」ですが、OAuth2を使用したログインなどでPersoniumにアクセスするアプリも実装可能です。(後者は「Personiumを使ったアプリ」と呼ばれます。)

手順

personium-blank-app

上記リポジトリから、ソースコードをクローンします。

設定ファイルの更新

ビルド・デプロイ設定

config.example.jsconfig.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.jsonsrc/assets/launch.json へリネームし、修正します。

{
  "personal": {
    "web": "<CELL_FQDN>/__/front/app",
    "android": "***:",
    "ios": "***;"
  }
}
  • <CELL_FQDN> にはアプリセルのFQDNを入力します。

ユーザーセル内アプリ設定①

src/bar/00_meta/00_manifest.example.jsonsrc/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.xmlsrc/bar/00_meta/90_rootprops.xml へリネームします。

ビルド

barファイルのビルド

アプリ配信時に配布するbarファイルをビルドします。

npm run build-bar

dist/{アプリセル名}.bar というファイルが生成されていれば成功です。これをユーザーのセルでインストールします。
今回はAppマーケットからのインストールは利用できないので、ホームアプリのBOXインストール機能を使用します。

Install bar

アプリのビルド

アプリのビルドは下記コマンドで実行します。

npm run build-app

ビルドしたものは build フォルダ配下に配置されます。

デプロイ

ビルド生成物のアップロード

下記コマンドを実行することで先程のコマンドでビルドしたファイルをアップロードします。

npm run deploy

ACLの設定

ACLの設定は手動で行います。アプリセルのセルマネージャを開き、以下を実行します。

  1. /__/front の all に exec を付与します。
  2. Service /__/front 内のスクリプト launghSPA.js に ServicePath app という名前を付けます。 Service Configuration
  3. /__/public の all に read を付与します。

アプリ情報の開示設定

下記4ファイルの all に read を付与します。

  • launch.json
  • profile.json
  • relations.json
  • roles.json

実行

barをインストールしたユーザーのホームアプリからアイコンをクリックするとアプリが起動します。

Home

Launch App

このとき、実行されるのは、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を書くかもしれません。

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

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.名前で受け取る。

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

Redux stateにimmutable.jsを使う

immutable.jsをRedux stateに使ってみました。本記事は簡単な使い方の紹介になります。
Immutable collections for JavaScript

Immutabilityはアプリのパフォーマンスに貢献します。加えてプログラミングやデバッグをよりシンプルにしてくれます。

ImmutabilityとReduxの関連は、以下の記事に詳しく書いてあります。
Redux FAQ: Immutable Data

Todoアプリ

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 src

4個のファイルを修正しました。必要に応じて、オリジナルと比べてみてください。ざっくり言って、コードが短くなっています。

state.todosをMapのListとして利用していきます。

src/redux/reducers/todos.js
import { 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.js
import { 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.js
import 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.js
import 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);

今回は以上です。

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

【Firebase Hosting】SPAなサイトでPage Not foundとでた

React.js(SPA)で構築したサイトをFirebase Hostingを用いてデプロイすると、
以下のように、404 Page Not Foundと表示された。

スクリーンショット 2019-07-16 13.37.12.png

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"
} ],

参照: 公式ドキュメント

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

reactのソースをビルドして公開する

作業ログ

$ npx create-react-app react-build-sumple --typescript
$ npm run build

とやってbuildディレクトリに生成されたindex.htmlをブラウザで開くと真っ白になる。

package.jsonにhomepageというプロパティを追加

  "name": "react-build-sumple",
  "version": "0.1.0",
  "private": true,
  "homepage": "./",

再度ビルドするとブラウザで表示出来るようになりました。

参考

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

EmmetをJSX内で利用する方法(VSCode)

概要

VSCodeは初期設定の状態でもEmmetが使えるようになっていますが、JSX内では下記のように使用できない。

初期設定でdivタグ入力時
スクリーンショット 2019-07-16 0.02.15.png

設定を変更して、emmetを機能するようにしていきます。

設定

1.設定を開く

VSCode内で
Mac: Command + ,
Windows: Ctrl + ,

2.設定変更

下記の手順で設定変更

①拡張機能
②Emmet
③setting.jsonで編集

UNADJUSTEDNONRAW_thumb_5.jpg

setting.jsonに設定を追加

setting.json
{
  "emmet.includeLanguages": {
     "javascript": "javascriptreact"
  }
}

これでdivを入力後tabを押すと以下のようになります。
n52og-b8vsz.gif

以上です。

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