–save-dev は、ローカルインストール
-g がグローバルインストール
-gをつけないと、ローカルインストールになる
ローカルインストールの場合は、node_modulesにパッケージがインストールされる
ローカルに入れると、package.jsonのdevDependenciesに追記される
ソフトウェアエンジニアの技術ブログ:Software engineer tech blog
随机应变 ABCD: Always Be Coding and … : хороший
–save-dev は、ローカルインストール
-g がグローバルインストール
-gをつけないと、ローカルインストールになる
ローカルインストールの場合は、node_modulesにパッケージがインストールされる
ローカルに入れると、package.jsonのdevDependenciesに追記される
webpack.config.js
const webpack = require('webpack'); module.exports = { target: 'node', entry: `./entry.js`, output: { path: `${__dirname}/output/`, filename: "main.js" }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", } ] } };
[vagrant@localhost front]$ npm run build
> @ build /home/vagrant/front
> webpack
Hash: eb8d6365b20e87793006
Version: webpack 4.41.2
Time: 421ms
Built at: 2019-11-20 13:15:11
1 asset
Entrypoint main = main.js
[0] ./entry.js 2 KiB {0} [built] [failed] [1 error]
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
ERROR in ./entry.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: /home/vagrant/front/entry.js: Cannot read property ‘bindings’ of null
at Scope.moveBindingTo (/home/vagrant/front/node_modules/@babel/traverse/lib/scope/index.js:822:13)
at BlockScoping.updateScopeInfo (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:364:17)
at BlockScoping.run (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:330:12)
at PluginPass.BlockStatementSwitchStatementProgram (/home/vagrant/front/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:70:24)
at newFn (/home/vagrant/front/node_modules/@babel/traverse/lib/visitors.js:179:21)
at NodePath._call (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:55:20)
at NodePath.call (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:42:17)
at NodePath.visit (/home/vagrant/front/node_modules/@babel/traverse/lib/path/context.js:90:31)
at TraversalContext.visitQueue (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:112:16)
at TraversalContext.visitSingle (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:84:19)
at TraversalContext.visit (/home/vagrant/front/node_modules/@babel/traverse/lib/context.js:140:19)
at Function.traverse.node (/home/vagrant/front/node_modules/@babel/traverse/lib/index.js:84:17)
at traverse (/home/vagrant/front/node_modules/@babel/traverse/lib/index.js:66:12)
at transformFile (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:119:29)
at runSync (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:48:5)
at runAsync (/home/vagrant/front/node_modules/@babel/core/lib/transformation/index.js:35:14)
at process.nextTick (/home/vagrant/front/node_modules/@babel/core/lib/transform.js:34:34)
at processTicksAndRejections (internal/process/task_queues.js:79:9)
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! @ build: `webpack`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the @ build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/vagrant/.npm/_logs/2019-11-20T04_15_11_600Z-debug.log
webconfigで書くのはわかったけど、なんか上手くいかんな。。
ブラウザにまだサポートされていないようなJSの次世代標準機能をブラウザで使えるようにするNode.jsトランスパイラ
ES6, ES7など(ECMAScript)
Bableを使う際に書くのはAltJSではなく、純粋なJavaScript
Bable ES6の構文サポート
– Class, Template Strings, Arrow Function, Default Parameter, Rest Parameter, Property Literals, Modules
class Code { constructor(item){ this.item = item; } showItem(){ console.log(`you are buying ${this.item}`); } } class Sec extends Code { constructor(item = 'コロプラ'){ super(item); } } var app = new Sec(); app.showItem();
[vagrant@localhost front]$ node entry.js
you are buying コロプラ
では、早速Babelをインストールしましょう。
$ npm install -g babel-cli
$ babel entry.js –out-file output.js
あれ、全然変わってない?
presetをインストールして、.babelrcを書く必要がある??
$ npm install –save-dev babel-preset-env
.babelrc
{ "presets": ["env"] }
再度実行
$ babel entry.js –out-file output.js
output.js
'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Code = function () { function Code(item) { _classCallCheck(this, Code); this.item = item; } _createClass(Code, [{ key: 'showItem', value: function showItem() { console.log('you are buying ' + this.item); } }]); return Code; }(); var Sec = function (_Code) { _inherits(Sec, _Code); function Sec() { var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'コロプラ'; _classCallCheck(this, Sec); return _possibleConstructorReturn(this, (Sec.__proto__ || Object.getPrototypeOf(Sec)).call(this, item)); } return Sec; }(Code); var app = new Sec(); app.showItem();
なるほど、OK!
ES5への変換は、babel-preset-envが必要ってことね。
そんで、これ、webpackとどう連携するの?
import { increment } from './modules/increment'; console.log('before:', 0); console.log('**increment**'); console.log('after', increment(0));
[vagrant@localhost front]$ npm run build && node ./output/main.js
> @ build /home/vagrant/front
> webpack
Hash: bd6e844b721278940d6c
Version: webpack 4.41.2
Time: 147ms
Built at: 2019-11-20 01:08:18
Asset Size Chunks Chunk Names
main.js 1.01 KiB 0 [emitted] main
Entrypoint main = main.js
[0] ./entry.js + 1 modules 193 bytes {0} [built]
| ./entry.js 143 bytes [built]
| ./modules/increment.js 50 bytes [built]
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
before: 0
**increment**
after 2
使い方わかってきた^^
webpack.config.js
module.exports = { entry: `./src/index.js`, output: { path: `${__dirname}`, filename: "main.js" } }
[vagrant@localhost front]$ npm run build
> @ build /home/vagrant/front
> webpack
Hash: e4eb3bfa6713e19351bb
Version: webpack 4.41.2
Time: 92ms
Built at: 2019-11-20 00:02:50
Asset Size Chunks Chunk Names
main.js 982 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 104 bytes {0} [built]
| ./src/index.js 41 bytes [built]
| ./src/sub.js 63 bytes [built]
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
おおおおおおおおおおおおおおお
なるほど、webpackも必須やな
こう書けば良い
package.json
{ "scripts": { "build": "webpack" }, "devDependencies": { "webpack": "^4.41.2", "webpack-cli": "^3.3.10" } }
すると、npx webpackと同じことがnpm run buildでできる。
[vagrant@localhost front]$ npm run build
> @ build /home/vagrant/front
> webpack
Hash: e9a08af5f50ea936c82f
Version: webpack 4.41.2
Time: 95ms
Built at: 2019-11-19 23:58:48
Asset Size Chunks Chunk Names
main.js 982 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 104 bytes {0} [built]
| ./src/index.js 41 bytes [built]
| ./src/sub.js 63 bytes [built]
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
WebpackはNode.jsでサーバーサイドで動かすモジュールハンドラー
-npmはJSライブラリのバージョン管理
-Bowerはフロントエンドのライブラリ管理
-Gulp:タスクランナー
-webpackはJSファイルのコーディングで手助け(JS,CSSのバンドリング)
https://webpack.js.org/
ん?npmとは違うのね。
Webpackもlessやsassのコンパイラツールのようなもの。
え?それなら、gulpはいらない??
gulpはsassのコンパイル
webpackは自分で書いたJSとライブラリのjsを一つにまとめる
JSで読み込むライブラリもまとめる
JS, CSS、画像ファイルをDataURI(base64)で一つのjsにまとめる
うん、なんかイマイチよくわからない。
複数のJSファイルをまとめられる、ってことはわかった。
Babelも用いる
とりあえず、npm initから
[vagrant@localhost front]$ npm init -y
Wrote to /home/vagrant/front/package.json:
{
“name”: “front”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1″
},
“keywords”: [],
“author”: “”,
“license”: “ISC”
}
# インストール
$ npm i -D webpack webpack-cli
main.js
import { hello } from "./sub"; hello();
sub/sub.js
export function hello(){ alert("hello method exection"); }
[vagrant@localhost front]$ npx webpack
Hash: e9a08af5f50ea936c82f
Version: webpack 4.41.2
Time: 163ms
Built at: 2019-11-19 23:43:38
Asset Size Chunks Chunk Names
main.js 982 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 104 bytes {0} [built]
| ./src/index.js 41 bytes [built]
| ./src/sub.js 63 bytes [built]
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="main.js"></script> </head> <body> </body> </html>
なんだこりゃ、あーこれか。webpackで書かれてたのか。なるほどねー
main.js
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t),alert("hello method exection")}]);
カレンダー機能の要件
– 大画面で表示用
– formのカレンダーはdate pickerを使用する
react-calendarというlibraryがあるので、これをまず試したい
https://github.com/wojtekmaj/react-calendar
Installation Add React-Calendar to your project by executing npm install react-calendar or yarn add react-calendar.
# npmでインストール
の前に、
[vagrant@localhost calendar]$ npm -v
1.3.6
[vagrant@localhost calendar]$ node -v
v0.10.48
その前にnodeとnpmをアップグレードしたい
[vagrant@localhost calendar]$ ll /etc/yum.repos.d/ | grep node
-rw-r–r–. 1 root root 464 9月 19 14:20 2014 nodesource-el.repo
[vagrant@localhost calendar]$ cat /etc/yum.repos.d/nodesource-el.repo
[nodesource]
name=Node.js Packages for Enterprise Linux 6 – $basearch
baseurl=https://rpm.nodesource.com/pub/el/6/$basearch
removeして最新を入れたい
$ sudo yum -y remove nodejs
$ sudo rm /etc/yum.repos.d/nodesource-el.repo
$ sudo yum install https://rpm.nodesource.com/pub_11.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm
$ ls -1 /etc/yum.repos.d
$ sudo yum install nodejs -y
[vagrant@localhost calendar]$ node -v
v11.15.0
[vagrant@localhost calendar]$ npm -v
6.7.0
react-calendarインストール
npm install react-calendar
あれ、その前にもう一回React.jsとwebpackを学ばないといかんやん。
ぎゃーーーーーーー
function TodoHeader(props){ const remaining = props.todos.filter(todo => { return !todo.isDone; }); return ( <h1> My Todos <span>({remaining.length}/{props.todos.length})</span> </h1> ); }
<script type="text/babel"> (() => { const todos = []; function TodoHeader(props) { const remaining = props.todos.filter(todo => { return !todo.isDone; }); return ( <h1> <button onClick={props.purge}>Purge</button> My Todos <span>({remaining.length}/{props.todos.length})</span> </h1> ); } function TodoItem(props){ return ( <li key={props.todo.id}> <label> <input type="checkbox" checked={props.todo.isDone} onChange={() => props.checkTodo(props.todo)} /> <span className={props.todo.isDone ? 'done' : ''}> {props.todo.title} </span> </label> <span className="cmd" onClick={() => props.deleteTodo(props.todo)}>[x]</span> </li> ); } function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} deleteTodo={props.deleteTodo} /> ); }); return ( <ul> {props.todos.length ? todos: <li>nothing to do</li>} </ul> ); } function TodoForm(props){ return ( <form onSubmit={props.addTodo}> <input type="text" value={props.item} onChange={props.updateItem}/> <input type="submit" value="Add"/> </form> ); } function getUniqueId(){ return new Date().getTime().toString(36) + '-' + Math.random().toString(36); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos, item: '' }; this.checkTodo = this.checkTodo.bind(this); this.deleteTodo = this.deleteTodo.bind(this); this.updateItem = this.updateItem.bind(this); this.addTodo = this.addTodo.bind(this); this.purge = this.purge.bind(this); } purge() { if (!confirm('are you sure?')) { return; } const todos = this.state.todos.filter(todo => { return !todo.isDone; }); this.setState({ todos: todos }); } addTodo(e){ e.preventDefault(); if(this.state.item.trim()===''){ return; } const item = { id: getUniqueId(), title: this.state.item, isDone: false }; const todos = this.state.todos.slice(); todos.push(item); this.setState({ todos: todos, item: '' }); } deleteTodo(todo){ if (!confirm('are you sure?')) { return; } const todos = this.state.todos.slice(); const pos = this.state.todos.indexOf(todo); todos.splice(pos, 1); this.setState({ todos: todos }); } checkTodo(todo){ const todos = this.state.todos.map(todo => { return {id: todo.id, title: todo.title, isDone: todo.isDone}; }); const pos = this.state.todos.map(todo=>{ return todo.id; }).indexOf(todo.id); todos[pos].isDone = !todos[pos].isDone; this.setState({ todos: todos }); } updateItem(e){ this.setState({ item: e.target.value }) } componentDidUpdate(){ localStorage.setItem('todos', JSON.stringify(this.state.todos)); } componentDidMount(){ this.setState({ todos: JSON.parse(localStorage.getItem('todos')) || [] }); } render(){ return ( <div className="container"> <TodoHeader todos={this.state.todos} purge={this.purge} /> <TodoList todos={this.state.todos} checkTodo={this.checkTodo} deleteTodo={this.deleteTodo} /> <TodoForm item={this.state.item} updateItem={this.updateItem} addTodo={this.addTodo} /> </div> ); } } ReactDOM.render( <App/>, document.getElementById('root') ); })(); </script>
<script type="text/babel"> (() => { const todos = [ {id: 0, title: 'Task 0', isDone: false}, {id: 1, title: 'Task 1', isDone: false}, {id: 2, title: 'Task 2', isDone: true} ]; function TodoItem(props){ return ( <li key={props.todo.id}> <label> <input type="checkbox" checked={props.todo.isDone} onChange={() => props.checkTodo(props.todo)} /> <span className={props.todo.isDone ? 'done' : ''}> {props.todo.title} </span> </label> </li> ); } function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} /> ); }); return ( <ul> {todos} </ul> ); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos }; this.checkTodo = this.checkTodo.bind(this); } checkTodo(todo){ const todos = this.state.todos.map(todo => { return {id: todo.id, title: todo.title, isDone: todo.isDone}; }); const pos = this.state.todos.map(todo=>{ return todo.id; }).indexOf(todo.id); todos[pos].isDone = !todos[pos].isDone; this.setState({ todos: todos }); } render(){ return ( <div> <h1>My Todo</h1> <TodoList todos={this.state.todos} checkTodo={this.checkTodo} /> </div> ); } } ReactDOM.render( <App/>, document.getElementById('root') ); })(); </script>
<script type="text/babel"> (() => { const todos = [ {id: 0, title: 'Task 0', isDone: false}, {id: 1, title: 'Task 1', isDone: false}, {id: 2, title: 'Task 2', isDone: true} ]; function TodoItem(props){ return ( <li key={props.todo.id}> <label> <input type="checkbox" checked={props.todo.isDone} onChange={() => props.checkTodo(props.todo)} /> <span className={props.todo.isDone ? 'done' : ''}> {props.todo.title} </span> </label> <span className="cmd" onClick={() => props.deleteTodo(props.todo)}>[x]</span> </li> ); } function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} deleteTodo={props.deleteTodo} /> ); }); return ( <ul> {todos} </ul> ); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos }; this.checkTodo = this.checkTodo.bind(this); this.deleteTodo = this.deleteTodo.bind(this); } checkTodo(todo){ const todos = this.state.todos.map(todo => { return {id: todo.id, title: todo.title, isDone: todo.isDone}; }); const pos = this.state.todos.map(todo=>{ return todo.id; }).indexOf(todo.id); todos[pos].isDone = !todos[pos].isDone; this.setState({ todos: todos }); } render(){ return ( <div className="container"> <h1>My Todo</h1> <TodoList todos={this.state.todos} checkTodo={this.checkTodo} deleteTodo={this.deleteTodo} /> </div> ); } } ReactDOM.render( <App/>, document.getElementById('root') ); })(); </script>
function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} deleteTodo={props.deleteTodo} /> ); }); return ( <ul> {props.todos.length ? todos: <li>nothing to do</li>} </ul> ); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos }; this.checkTodo = this.checkTodo.bind(this); this.deleteTodo = this.deleteTodo.bind(this); } deleteTodo(todo){ if (!confirm('are you sure?')) { return; } const todos = this.state.todos.slice(); const pos = this.state.todos.indexOf(todo); todos.splice(pos, 1); this.setState({ todos: todos }); }
function TodoItem(props){ return ( <li key={props.todo.id}> <label> <input type="checkbox" checked={props.todo.isDone} onChange={() => props.checkTodo(props.todo)} /> <span className={props.todo.isDone ? 'done' : ''}> {props.todo.title} </span> </label> <span className="cmd" onClick={() => props.deleteTodo(props.todo)}>[x]</span> </li> ); } function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} deleteTodo={props.deleteTodo} /> ); }); return ( <ul> {props.todos.length ? todos: <li>nothing to do</li>} </ul> ); } function TodoForm(props){ return ( <form> <input type="text" value={props.item} onChange={props.updateItem}/> <input type="submit" value="Add"/> </form> ); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos, item: '' }; this.checkTodo = this.checkTodo.bind(this); this.deleteTodo = this.deleteTodo.bind(this); this.updateItem = this.updateItem.bind(this); } deleteTodo(todo){ if (!confirm('are you sure?')) { return; } const todos = this.state.todos.slice(); const pos = this.state.todos.indexOf(todo); todos.splice(pos, 1); this.setState({ todos: todos }); } checkTodo(todo){ const todos = this.state.todos.map(todo => { return {id: todo.id, title: todo.title, isDone: todo.isDone}; }); const pos = this.state.todos.map(todo=>{ return todo.id; }).indexOf(todo.id); todos[pos].isDone = !todos[pos].isDone; this.setState({ todos: todos }); } updateItem(e){ this.setState({ item: e.target.value }) } render(){ return ( <div className="container"> <h1>My Todo</h1> <TodoList todos={this.state.todos} checkTodo={this.checkTodo} deleteTodo={this.deleteTodo} /> <TodoForm item={this.state.item} updateItem={this.updateItem} /> </div> ); } }
<script type="text/babel"> (() => { const todos = []; function TodoItem(props){ return ( <li key={props.todo.id}> <label> <input type="checkbox" checked={props.todo.isDone} onChange={() => props.checkTodo(props.todo)} /> <span className={props.todo.isDone ? 'done' : ''}> {props.todo.title} </span> </label> <span className="cmd" onClick={() => props.deleteTodo(props.todo)}>[x]</span> </li> ); } function TodoList(props){ const todos = props.todos.map(todo => { return( <TodoItem key={todo.id} todo={todo} checkTodo={props.checkTodo} deleteTodo={props.deleteTodo} /> ); }); return ( <ul> {props.todos.length ? todos: <li>nothing to do</li>} </ul> ); } function TodoForm(props){ return ( <form onSubmit={props.addTodo}> <input type="text" value={props.item} onChange={props.updateItem}/> <input type="submit" value="Add"/> </form> ); } function getUniqueId(){ return new Date().getTime().toString(36) + '-' + Math.random().toString(36); } class App extends React.Component { constructor(){ super(); this.state = { todos: todos, item: '' }; this.checkTodo = this.checkTodo.bind(this); this.deleteTodo = this.deleteTodo.bind(this); this.updateItem = this.updateItem.bind(this); this.addTodo = this.addTodo.bind(this); } addTodo(e){ e.preventDefault(); if(this.state.item.trim()===''){ return; } const item = { id: getUniqueId(), title: this.state.item, isDone: false }; const todos = this.state.todos.slice(); todos.push(item); this.setState({ todos: todos, item: '' }); } deleteTodo(todo){ if (!confirm('are you sure?')) { return; } const todos = this.state.todos.slice(); const pos = this.state.todos.indexOf(todo); todos.splice(pos, 1); this.setState({ todos: todos }); } checkTodo(todo){ const todos = this.state.todos.map(todo => { return {id: todo.id, title: todo.title, isDone: todo.isDone}; }); const pos = this.state.todos.map(todo=>{ return todo.id; }).indexOf(todo.id); todos[pos].isDone = !todos[pos].isDone; this.setState({ todos: todos }); } updateItem(e){ this.setState({ item: e.target.value }) } render(){ return ( <div className="container"> <h1>My Todo</h1> <TodoList todos={this.state.todos} checkTodo={this.checkTodo} deleteTodo={this.deleteTodo} /> <TodoForm item={this.state.item} updateItem={this.updateItem} addTodo={this.addTodo} /> </div> ); } } ReactDOM.render( <App/>, document.getElementById('root') ); })(); </script>
各パーツをそれぞれコンポートネントとして切り分ける。