Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React はじめの一歩

Avatar for pvcresin pvcresin
February 13, 2018

React はじめの一歩

# 目標
- Reactの概念を何となく理解する
- 軽めのReactを書けるようになる
- (Nodeのサーバを建てられるようになる)

https://github.com/pvcresin/reactMailFormDemo

Avatar for pvcresin

pvcresin

February 13, 2018
Tweet

More Decks by pvcresin

Other Decks in Programming

Transcript

  1. 事前準備 使うもの Google Chrome, Visual Studio Code,Yarn, Node.js (v8.x) ちなみに

    今回,最終的にできるファイルはここ 前回のスライドを読み,ツールを使いこなせる前 提で進めていきます 3
  2. Yarnの復習 yarn init = package.jsonを作る yarn = 必要なモジュールを全部一気に入れる yarn add

    xxx = 依存ライブラリ xxx を入れる yarn add -D xxx = 開発用ライブラリ xxx を入れる yarn xxx = npm scriptのタスク xxx を実行 5
  3. JavaScript(ES6)の復習 変数宣言:再代入不可の const , 再代入可の let アロー関数(≒即時関数・ラムダ式) // 従来の関数 var

    f = function(x) { return x * 2 } // アロー関数 const f = (x) => { return x * 2 } const f = (x) => x * 2 // 1行なら中カッコ{}はずせる const f = x => x * 2 // 引数1つならカッコ()不要 6
  4. クラス構文 (内部的には関数に変換されるらしい) class Person { constructor(name) { this.name = name

    } hello() { console.log(`My name is ${this.name}.`) } } const p = new Person('es6') p.hello() //=> "My name is es6." 7
  5. import / export person.js export default class Person { }

    index.js `import Person from './person' importすることでindex.jsでもPersonクラスが使 える 8
  6. 例えば? 画面のリスト [a, b] を 新しいリスト [a, c] に更新 <li>a</li>

    <li>a</li> <li>b</li> --- 更新 --> <li>c</li> 従来 現在の <li> タグを全て取得し,一つ一つを新し いデータと比較して書き換える 生のDOMを使って比較するので処理が重い 仮想DOM 全自動 12
  7. JSX記法 JSの言語拡張,JS内にHTMLタグが書けるイメージ class App extends Component { render() { return

    ( <div>Hello React</div> ) } } render(<App />, document.querySelector('main')) 拡張子は .jsx ( .js でもよさそう) class はJSで予約語のため, className と書く 13
  8. 準備 VSC設定 "editor.formatOnSave": true, "files.trimTrailingWhitespace": true, Chrome拡張 React Dev Tools

    拡張 からファイルへのアクセスを許可しとく プロジェクトフォルダを作り, yarn init -y する 14
  9. モジュールのインストール package.json を先に写し, yarn する { "name": "reactMailFormDemo", "version": "1.0.0",

    "main": "index.js", "license": "MIT", "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "webpack": "^3.9.1" }, 15
  10. "dependencies": { "autobind-decorator": "^2.1.0", "body-parser": "^1.18.2", "express": "^4.16.2", "react": "^16.2.0",

    "react-dom": "^16.2.0", "react-json-view": "^1.16.0" }, "scripts": { "start": "node server.js", "build": "webpack", "watch": "npm run build -- -w" } } 16
  11. 開発モジュール webpack : JS合体君・バンドラ babel-core : JSを変換する(バベる)君 babel-loader : webpack上でバベれるようにする君

    babel-preset-es2015 : ES6 ➜ ES5 babel-preset-react : JSX ➜ 普通のJS babel-plugin-transform-decorators-legacy : @ (デコレータ)を使えるようにする君 18
  12. jscon g.json { "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true, "module":

    "es6", "target": "ES6" }, "exclude": [ "node_modules" ] } プロジェクトルートに置いておく 今後一切触らない 20
  13. index.html <!DOCTYPE html> <html lang='ja'> <head> <title>Mail Form</title> <meta charset='UTF-8'>

    <link rel='stylesheet' href='style.css'> </head> <body> <main></main> <script src='index.js'></script> </body> </html> style.cssは空で大丈夫 21
  14. webpack.con g.js src/index.jsx を dist に吐き出す JSでimportする時に拡張子 js , jsx

    を省略する const webpack = require('webpack') const path = require('path') module.exports = { entry: { index: path.join(__dirname, './src/index.jsx') }, output: { path: path.join(__dirname, 'dist/'), filename: '[name].js' }, 22
  15. module: { rules: [{ test: /\.js|jsx?$/, exclude: /node_modules/, loader: 'babel-loader',

    options: { presets: ['react', 'es2015'], plugins: ['transform-decorators-legacy'] } }] }, resolve: { extensions: ['.js', '.jsx'] } } jsxのファイルに書かれたreactやらes6やらデコレ ータを普通のJSに変換する 23
  16. index.jsx import React, { Component } from 'react' import ReactDOM,

    { render } from 'react-dom' render(<h1>React</h1>, document.querySelector('main')) index.html 内の main タグに h1 タグをマウント(レンダリング)する 24
  17. Reactを動かしてみる npm script yarn build : webpackでJSを1度だけビルド yarn watch :

    変更を検知してビルドし直し続ける yarn watch して dist/index.html を開いてみよう h1タグがレンダリングされているはず 25
  18. コンポーネントを作る src/components/App.jsx を作る import React, { Component } from 'react'

    import ReactDOM from 'react-dom' export default class App extends Component { constructor(props) { super(props) } render() { // 実際のAppタグの中身 return ( <div>App</div> ) } } 26
  19. index.jsx import React, { Component } from 'react' import ReactDOM,

    { render } from 'react-dom' import App from './components/App' render(<App />, document.querySelector('main')) yarn watch して,ブラウザをリロードしたら main タグの中に App タグがマウントされる 27
  20. autobind-decorator を使う イベントの度に .bind(this) を書かなくてもOKに import autobind from 'autobind-decorator' //

    importして @autobind // ってつけると submit() { /* 処理 */ } // .bind(this)がなくなってスッキリ <button onClick={this.submit}>submit</button> 29
  21. Props 親コンポーネントから渡されたプロパティ(不変) 例:Todoのリストを表示するアプリ App ➜ (list) ➜ TodoList ➜ (text)

    ➜ Todo Propsの渡し方 <Todo text='買い物' /> Stateを持たないコンポーネントは関数でも書ける const Todo = props => <div>{props.text}</div> 30
  22. Stateを使ってみる constructor(props) { super(props) this.state = { // stateを宣言&初期値を設定 name:

    '' } } @autobind editName(e) { // イベントe -> テキストをstateのnameに保存 this.setState({ name: e.currentTarget.value }) } <input type='text' onChange={this.editName} value={this.state.name} /> コンポーネント内のデータと見た目が結びついた 32
  23. メールフォームを作る これまでを踏まえて,Stateに name と text を持ち, 見た目と結びついたコンポーネントを書いてみる import React, {

    Component } from 'react' import ReactDOM from 'react-dom' import autobind from 'autobind-decorator' export default class App extends Component { constructor(props) { super(props) this.state = { name: '', text: '' } } 33
  24. @autobind editName(e) { this.setState({ name: e.currentTarget.value }) } @autobind editText(e)

    { this.setState({ text: e.currentTarget.value }) } @autobind submit() { alert( `氏名: ${this.state.name} \n内容: ${this.state.text}` ) } 続く 34
  25. render() { return ( <div> <p> <label>name</label> <input type='text' onChange={this.editName}

    value={this.state.name} /> </p> <p> <label>text</label> <textarea onChange={this.editText} value={this.state.text}></textarea> </p> <button onClick={this.submit}>submit</button> </div> ) } } 35
  26. server.js をプロジェクトルートに作成 const express = require('express') const bodyParser = require('body-parser')

    express() .use(express.static('dist')) .use(bodyParser.urlencoded({ extended: true })) .use(bodyParser.json()) .post('/sendContactMessage', (req, res) => { console.log(req.body) res.json({ server_receive_time: new Date().toString(), name: req.body.name, text: req.body.text, }) }) .listen(3000, () => { console.log('http://localhost:3000') }) 39
  27. サーバを起動 1. yarn watch とは別のターミナルで yarn start yarn watch :

    jsxをjsに変換し続ける yarn start : dist フォルダをserveし続ける 2. http://localhost:3000 にアクセス あとはクライアントからデータを送信するだけ 41
  28. 何が良いか 非同期処理を行う関数A, B, Cがあるとする BにはAの結果が必要,CにはBの結果が必要 A(function(a) { // Aの結果が帰ってきた時のCallback B(a,

    function(b){ // Bの..Callback C(b, function(c){ // Cの...Callback done(c) // 最終的な結果 }) }) }) どんどんネストが深くなっていく... 43
  29. fetchとは GETとかPOSTする時にPromise型で処理できる関数 使い方 fetch(url, { method: 'POST', body: /* 送信データ

    */ }).then(response => { return response.json() // 結果をJSON形式に変換 }).then(json => { /* 結果のJSONを使った処理 */ }) お疲れさまXMLHttpRequest、こんにちはfetch 45
  30. 通信準備 this.state = { name: '', text: '', result: {}

    } stateに result を定義 初期値は空のオブジェクト {} を設定 46
  31. fetchを使ってみる @autobind submit() { const url = './sendContactMessage' fetch(url, {

    headers: { 'Content-Type': 'application/json' }, method: 'POST', body: JSON.stringify({ name: this.state.name, text: this.state.text }) }) .then(response => response.json()) .then(json => { this.setState({ result: json }) }) } 47
  32. 通信完成 1. submitボタンを押す 2. POST 3. サーバからレスポンス 4. JSONに変換 5.

    stateの result に保存 Dev-Tool のReactタブで確認! できた~~~~!! 48
  33. react-json-viewを使ってみる import ReactJson from 'react-json-view' <ReactJson src={this.state.result} /> を button

    タグの後に入れてみる Propsとして src で渡したJSONの構造が表示される 50