【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); }
通常であれば、ボタンクリック時にファイルダイアログが開き、
ファイルが選択された状態でダイアログの「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
プロパティとして取得できます。
【shell】カラフルにcatする
はい
$ brew install lolcat
【React】Material-UIでReactアプリケーションをマテリアルデザイン化する
Material-UI
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
とりあえずAppBar
とCard
、Button
を使ったレイアウトを簡単に作ってみました。
既存の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します。
withStyles
はCSSによる要素のスタイリングに使います。
AppBar
<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。オプションによって様々な種類
のボタンを選べます。
FloatingActionButton
FloatingActionButtonもちゃんと用意してあるみたいです。
中のアイコンは別途インストールします。
$ 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コンポーネントが用意されているので、色々試してみたいと思います。
参考
【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が発生するたびにログが表示されていることがわかります。
拡張
redux-logger
は今のままでも様々な情報を取得できますが、より詳細なカスタマイズを行うことも可能です。下記リポジトリに詳細なUsage
が記載されているので、もしもっとloggerをいじって見たい人は参考にするといいかもしれません。
【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!」と表示されていることがわかります。
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】ターミナル上で蒸気機関車を呼ぶ
【React】JSX
※ここで述べるJSXはReact.jsで利用されるものを指します。
JSXとは
JSXはFacebook社が開発した、Reactの内部でHTML記述をより簡潔に書くためのJavaScript拡張構文です。
ReactではHTMLの出力を主にJavaScript上で定義します。というのも、Reactはコンポーネント指向を想定したライブラリであるためです(下記リンク参考)。
コンポーネント指向とは、「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のインストール
ツールをインストールするため、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
でアプリを立ち上げるとブラウザが起動し、アプリケーションが表示されます。
表示できました!
このアプリケーションの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;