Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【HTML】Safariで<input type="file" />で二度同じファイルをアップロードしようとすると、二回目のonchangeが発火しない

メモ。

HTMLでファイルダイアログを開いて指定したファイルを操作するとき、
以下のように<input type="file" />にonchange属性をつけ、そこにイベントハンドラ関数を設定します。

HTML
<div ng-app="myApp">
  <div ng-controller="MyAppCtrl">
    <input type="file" onchange="onChange(this)"/>
  </div>
</div>
JavaScript
function onChange(e) {
  console.log(e.value);
}

codepen.io

通常であれば、ボタンクリック時にファイルダイアログが開き、
ファイルが選択された状態でダイアログの「OK」ボタンを押すとonchangeに渡した関数が実行されます。

しかしSafariの場合、ダイアログでファイルを選択->OKを押した後(onchangeの関数実行)、
再びダイアログを開いて同じファイル選択->OKを押した場合に、onchangeに渡した関数が実行されません。

対策

同じような問題に引っかかっている方の記事が見つかりました。

https://qiita.com/te20/items/00a72535163c6d632408qiita.com

一度アップロードしたコンテンツをクリアしてあげれば、問題は解消されるようです。

<div ng-app="myApp">
  <div ng-controller="MyAppCtrl">
    <input type="file" onchange="onChange(this) onclick="onClick(this)"/>
  </div>
</div>
JavaScript
function onChange(e) {
  console.log(e.value);
}

function onClick(e) {
    e.target.value = "";
}

具体的には、inputタグに対応するDOMが持つvalueプロパティを空文字にしてあげます。
このinputタグのDOMはイベントハンドラ関数の引数からtargetプロパティとして取得できます。

【React】Material-UIでReactアプリケーションをマテリアルデザイン化する

Material-UI

material-ui.com

Material-UIはマテリアルデザインをReactアプリケーションに導入することができるUIコンポーネントです。Material-UIを使うことで、手軽にGoogleマテリアルデザインを踏襲したアプリケーションを作ることができます。

Install

例によってnpmでインストールします。

$ npm install @material-ui/core
# 他のUIコンポーネントも一括でインストールしたい場合
$ npm install @material-ui/core

ついでにReactのアプリも作ってしまいます。
create-react-appのインストールについてはこちら。

$ create-react-app material-react-app
$ cd material-react-app

Usage

f:id:rennnosukesann:20180522000854p:plain

とりあえずAppBarCardButtonを使ったレイアウトを簡単に作ってみました。

既存のApp.jsを編集します。

src/App.js
import React, { Component } from 'react';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';

const styles = {
  card: {
    margin: 48,
    height: 128
  },
};

class App extends Component {
  render() {

    const classes = this.props.classes;
    console.log(classes);

    return (
      <div>

        <AppBar position="static" color="default">
          <Toolbar>
            <Typography variant="title" color="inherit">
              AppBar
            </Typography>
          </Toolbar>
        </AppBar>

        <Card className={classes.card}>
          <CardContent>
            Hello, Material-UI in React :)
          </CardContent>
          <CardActions>
            <Button >Cancel</Button>
            <Button variant="raised" color="primary">OK</Button>
          </CardActions>
        </Card>

      </div>
     );
  }
}

export default withStyles(styles)(App);

軽く説明

import
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';

使用するUIコンポーネントは片っ端からimportします。
withStylesCSSによる要素のスタイリングに使います。

AppBar

f:id:rennnosukesann:20180522001426p:plain

        <AppBar position="static" color="default">
          <Toolbar>
            <Typography variant="title" color="inherit">
              AppBar
            </Typography>
          </Toolbar>
        </AppBar>

AppBar(一番上のバー)です。タイトルを表示したり、トップメニューを入れたりできます。基本ToolBar入れ子にして、その中に文字列やアイコンを配置していくようです。

Card&Button
        <Card className={classes.card}>
          <CardContent>
            Hello, Material-UI in React :)
          </CardContent>
          <CardActions>
            <Button >Cancel</Button>
            <Button variant="raised" color="primary">OK</Button>
          </CardActions>
        </Card>

コンテンツの一単位としてよく使われるカード。
CardContentでカードコンテンツの領域を、CardActionsでボタンなどアクションを要するコンポーネントの領域を確保します。

            <Button >Cancel</Button>
            <Button variant="raised" color="primary">OK</Button>

Buttonの配置はこれだけでOK。オプションによって様々な種類 のボタンを選べます。

f:id:rennnosukesann:20180522002227p:plainf:id:rennnosukesann:20180522002233p:plainf:id:rennnosukesann:20180522002238p:plain

FloatingActionButton

FloatingActionButtonもちゃんと用意してあるみたいです。

f:id:rennnosukesann:20180522003116p:plain

中のアイコンは別途インストールします。

$ npm install @material-ui/icons
import React, { Component } from 'react';
import Button from '@material-ui/core/Button';
import AddIcon from '@material-ui/icons/Add';
import Icon from '@material-ui/core/Icon';
import DeleteIcon from '@material-ui/icons/Delete';


class App extends Component {
  render() {

    return (
      <div>

        <div>
          <Button variant="fab" color="primary" aria-label="add">
            <AddIcon />
          </Button>
          <Button variant="fab" disabled aria-label="delete">
            <DeleteIcon />
          </Button>
        </div>

      </div>
     );
  }
}

export default App;

ほかにも様々なUIコンポーネントが用意されているので、色々試してみたいと思います。

参考

material-ui.com

【React】redux-loggerでActionやStateのログを出力する

redux-logger

ReduxではRedux Middelwareという様々な機能拡張が有志の手によって提供されています(プラグインのようなものです)。これから紹介するredux-loggerもRedux Middlewareの一つであり、これを使えばRedux上のActionディスパッチやStateの変化を容易にロギングすることができます。

インストールも簡単で、他のツール同様npmを使うだけです。

$ npm install --save redux-logger

Usage

既存のアプリケーションに挿入する前提で説明します。

まずはreduxからapplyMiddlewareと、redux-loggerからloggerをインポートします(createStoreは後のStore作成用)。

import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';

次に、Store生成時に呼ばれるcreateStoreの第二引数にapplyMiddleware(logger)を追加します。applyMiddlewareはRedux Middlewareを有効化するのに必要であり、任意個数のミドルウェアを引数に取ることができます。今回はredux-loggerのみ必要なので、loggerを引数に渡しています。

const store = createStore(tasksReducer, applyMiddleware(logger));

これでOKです。アプリケーションを起動し、Actionが発生するたびにログが表示されていることがわかります。

f:id:rennnosukesann:20180520142711p:plain

拡張

redux-loggerは今のままでも様々な情報を取得できますが、より詳細なカスタマイズを行うことも可能です。下記リポジトリに詳細なUsage が記載されているので、もしもっとloggerをいじって見たい人は参考にするといいかもしれません。

github.com

【React】React+JSXを用いてUIコンポーネントを作成する①

前回の記事でJSXについて書いたので、今回は簡単なUIコンポーネント作成の流れをメモします。

前準備

create-react-appをインストールし、Reactプロジェクトを作成します(アプリの立ち上げも行っておきます)。

$ brew install node
$ npm install -g create-react-app
$ mkdir YourReactProject; cd YourReactProjectDir
$ create-react-app my-app
$ cd my-app
$ npm start 

UIコンポーネントの雛形を作成

まずは簡単のため、既存のindex.jsを改変します。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

// HTML
const renderHTML =
<div>
  Hello, React!
</div>;

// 出力
ReactDOM.render(
  renderHTML, document.getElementById('root')
);

上記のコードを保存し実行結果のページを開くと、「Hello, React!」と表示されていることがわかります。

f:id:rennnosukesann:20180520015255p:plain

1,2行目のimportでは、react``react-domをインポートしています。react-domはHTMLを出力するのに必要です。

import React from 'react';
import ReactDOM from 'react-dom';

次に出力するHTMLをJSXの記法に従い記述しています。後ほどこのrenderHTMLを変更して出力するUIを編集していきます。

// HTML
const renderHTML =
<div>
  Hello, React!
</div>;

最後にHTMLの出力です。ReactDOM.renderメソッドは、第一引数に取るReact要素を第二引数に指定したDOMの子要素とします。

ReactDOM.render(
  renderHTML, document.getElementById('root')
);

【Shell】ターミナル上で蒸気機関車を呼ぶ

蒸気機関車とは

蒸気機関車(じょうききかんしゃ)とは、蒸気機関によって動く機関車のことである。日本では Steam Locomotive の頭文字をとって、SL(エスエル)とも呼ばれる。また、蒸気機関車、または蒸気機関車が牽引する列車のことを汽車とも言う[1]。また、明治時代には蒸気船に対して陸の上を蒸気機関で走ることから、「陸蒸気」(おかじょうき)とも呼んでいた。第二次世界大戦の頃までは「汽罐車」(きかんしゃ)という表記も用いられた(「汽罐」はボイラーの意)。 - Wikipediaより引用

Usage

$ brew install sl
$ sl
$ sl -l
$ sl -a

参考

蒸気機関車 - Wikipedia

【React】JSX

f:id:rennnosukesann:20180515230713p:plain:w300

※ここで述べるJSXはReact.jsで利用されるものを指します。

JSXとは

JSXはFacebook社が開発した、Reactの内部でHTML記述をより簡潔に書くためのJavaScript拡張構文です。

ReactではHTMLの出力を主にJavaScript上で定義します。というのも、Reactはコンポーネント指向を想定したライブラリであるためです(下記リンク参考)。

qiita.com

コンポーネント指向とは、「View(HTML)の構造・見た目と振る舞いを一つの単位とする」設計をメインに据える開発手法です。例えば、ログイン画面のログインボタンを作成する場合「ログインボタンの構造を定義するHTML」「ログインボタンのデザインを決めるCSS」「ログインボタンのロジックを定義するJS」をひとまとめにして作ります。

コンポーネント指向で設計する上で、ReactではHTML記述をJavaScript内部で行い、そこに変数や条件分岐などの動的なロジックを埋め込んでいきます。このHTML記述にJSXは用いられます。

Usage

では実際にどのようにHTMLを定義していくのでしょうか。
React標準のcreateElementを使えば、以下のようにHTMLを定義できます。

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      React.createElement('div', null, 
        React.createElement('p', null, 'Hello, JSX!'));
    );
  }
}

しかし、これではHTMLタグを新しく追加するたびにReact.createElementを呼び出さなくてはいけません。これでは冗長すぎます。

そこでJSXの出番です。
JSXによって、上記のHTML定義を以下のように書き換えることができます。

class App extends Component {
  render() {
    return (
      <div><p>Hello, JSX!</p></div>
    );
  }
}

スクリプト中に直接HTMLタグを書いてしまいましたが、JSXが導入されていれば問題なく解釈されます。 JSXを使うことで冗長なReact.createElementがなくなっただけでなく、見た目も自然なHTMLを記述することができました!

もちろんJavaScript内のできごとなので、JSXによって記述されたHTML内に式を仕込むことだってできます。式は{}で囲むことで挿入できます。

class App extends Component {
  render() {
    return (
      <div><p>現在の時刻 : {new Date().toLocaleFormat()}</p></div>
    );
  }
}

逆に、JavaScript内部に記述しているために普段のHTMLとは異なる書き方をしなければならない箇所があります。例えばタグを識別・修飾するための属性classは、JavaScript予約語とかぶるためclassNameと記述しなければなりません。

class App extends Component {
  render() {
    return (
      <div className="cls_hello_jsx"><p>Hello, JSX!</p></div>
    );
  }
}

Install

JSXがどのようなものかはわかりましたが、どのようにReact+JSXを始めればよいのでしょうか。

実はFacebook社が提供した開発ツールであるcreate-react-appを利用することで、ビルド設定などを行わずともすぐに開発を始めることができます。

Node.jsのインストール

※検証環境は MacOS X High Sierraです。

ツールをインストールするため、Node.jsを先にインストールします。

brew install node

Windowsの方はこちらからお願いします! ダウンロード | Node.js

Node.jsをインストールすることでパッケージマネージャnpmが使えるようになるので、早速create-react-appをインストールしていきましょう。

$ npm install -g create-react-app

Windowsの方はコマンドプロンプト上で実行をお願いします。

これでインストール完了です!
create-react-appコマンドが使えるようになります。

# 適当なディレクトリを作成
$ mkdir React-JSX; cd React-JSX
# アプリを作成
$ create-react-app my-app
# 作成完了
$ ls my-app
README.md          package-lock.json  public/
node_modules/      package.json       src/

作成したアプリはすぐ実行することができます。

$ cd my-app
# 開発モードで起動(localhost:3000)
$ npm start

npm startでアプリを立ち上げるとブラウザが起動し、アプリケーションが表示されます。

f:id:rennnosukesann:20180518003117p:plain

表示できました!

このアプリケーションのView(src/App.js)はすでにJSXで記述されています。JSXをいじり始める際はここを少しずつ改良していくのもありだと思います。

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

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

参考

reactjs.org

qiita.com

qiita.com

qiita.com