【React 17.0.2】vue.jsとの比較

– Vueの場合はv-ifやv-showなどがあるが、Reactの場合は、if や for などの独自の制御構文は存在せず、mapメソッドや論理演算子で構造を表現する
– Vueではv-bind:classで記述するが、Reactの場合は className 属性を用いる
– イベントは@clickとかではなく、onClick={*} などとHTMLに似た構文が用いられる
– Vueではdata()メソッドの返却によって定義するが、ReactではuseStateフックを用いる
– フォーム処理では、vueではv-modelを扱うが、Reactでは自作する必要がある
– Componentはメソッドで書く必要はない
– computedは useMemoメソッドで似たような表現ができる
– フィルター機能はないので、関数で書く
– createdはないので、returnより前がcreatedにあたる
– useEffectフックを使って、マウント後に実行したい処理を登録する。再描画の処理も同様

とっつきやすさはvueの方があるが、reactは基本機能を提供しているので、使いこなしていけばreactも居心地が良くなりそう

【React 17.0.2】textareaのinput

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>⚛️ React todo</title>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.8.2/css/bulma.min.css" />
	<style>
		* {
			margin: 0;
			padding: 0;
			box-sizing: border-box;
		}
		body {
			background-color: rgb(240, 240, 255);
		}
		.tweet {
			display: flex;
			background-color: white;
		}
		.tweet:not(:last-child){
			border-bottom: 1px solid rgb(200, 200, 200);
		}
		.icon-container {
			width: 1.5em;
			font-size: 3em;
			padding: 0.2em;
		}
		.body-container {
			flex: 1;
			padding: 0.5em;
		}
		.account-name {
			color: gray;
		}
	</style>
</head>
<body>
	<div id="app"></div>

	<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://unpkg.com/@babel/standalone/babel.min.js"></script>

	<script type="text/babel">
		function Tweet(props){
			return (
				<div className="tweet">
					<div className="icon-container">{props.icon}</div>
					<div className="body-container">
						<div className="status-display">
							<div className="display-name">{props.displayName}</div>
							<div className="account-name">@{props.accountName}</div>
						</div>
						<div className="content">{props.content}</div>
					</div>
				</div>
			)
		}

		function App(){
			return (
				<div>
					<Tweet 
						icon="👶"
						displayName="モカ"
						accountName="mocca"
						content="まあギャンブルだよね。期待はしてる。" 
					/>
					<Tweet 
						icon="🦸"
						displayName="いがこ"
						accountName="igaco"
						content="「稼ぐ力」の実態を見極めないと" 
					/>
				</div>
			);
		}

		const target = document.getElementById('app');
		ReactDOM.render(<App />, target);
	</script>
</body>

– 状態管理

const[value, setValue] = React.useState('defalt');
setValue('new value');

useStateはデフォルト値を与えると、前にコンポーネント関数を呼び出した時の値と、値を更新するための関数を返却

inputできるようにします。

	<script type="text/babel">
		function Tweet(props){
			const[liked, setLike] = React.useState(false);
			const toggleLike = React.useCallback(() => setLike((prev)=> !prev), [setLike]);

			return (
				<div className="tweet">
					<div className="icon-container">{props.icon}</div>
					<div className="body-container">
						<div className="status-display">
							<div className="display-name">{props.displayName}</div>
							<div className="account-name">@{props.accountName}</div>
						</div>
						<div className="content">{props.content}</div>
						<div className="status-action">
							<span onClick={toggleLike}>{liked ? '❤️':'♡'}</span>
						</div>
					</div>
				</div>
			)
		}

		function Timeline(props){
			const tweetList = props.tweets.map((tw) => (
				<Tweet
					key={tw.id}
					icon={tw.icon}
					displayName={tw.displayName}
					accountName={tw.accountName}
					content={tw.content}
				/>
			));

			return <div className="timeline">{tweetList}</div>;
		}

		function TweetInput(props){
			const textareaRef = React.useRef(null);

			const sendTweet = React.useCallback(() => {
				if(textareaRef.current){
					props.addTweet({
						id: new Date().getTime(),
						icon: '☠️',
						displayName: '龍神',
						accountName: 'dragon',
						content: textareaRef.current.value
					});
				}
			}, [textareaRef.current, props.addTweet]);

			return (
				<div>
					<div><textarea className="tweet-textarea" ref={textareaRef}></textarea></div>
					<div><button onClick={sendTweet} className="send-tweet">Tweet</button></div>
				</div>
			);
		}

		function App(){
			const [tweets, setTweets] = React.useState([
				{
					id: 0,
					icon: '👶',
					displayName: 'モカ',
					accountName: 'mocca',
					content: 'まあギャンブルだよね。期待はしてる。'
				},
				{
					id: 1,
					icon: '🦸',
					displayName: 'いがこ',
					accountName: 'igaco',
					content: '「稼ぐ力」の実態を見極めないと'
				},
			]);

			const addTweet = React.useCallback((tweet) => setTweets((prev)=> [tweet, ...prev]), [setTweets]);

			return (
				<div>
					<TweetInput addTweet={addTweet} />
					<Timeline tweets={tweets}/>
				</div>
			);
		}

		const target = document.getElementById('app');
		ReactDOM.render(<App />, target);
	</script>

なるほど〜 なんか色々できそうだね

【React 17.0.2】カスタムフック

useState, useEffectの他に、カスタムフックを自作することもできる

カスタムフックのメリット
– UIと切り離したロジック抽出
– コンポーネント関数の肥大化抑制
– 複数のフック呼び出しを理論的単位でまとめられる

<script src="https://unpkg.com/@popperjs/core@2"></script>
	<script src="https://unpkg.com/tippy.js@6"></script>

	<script type="text/babel">
		function useTooltip(id, content){
			React.useEffect(() => {
				tippy(`#${id}`, {content});
			}, [id, content]);
		}

		function App(){
			useTooltip('myButton', 'Hello world!');
			useTooltip('myParagraph', 'This is another tooltip.');

			return(
				<>
					<button id="myButton">Hover me</button>
					<p>
						<span id="myParagraph">Hover me too!</span> 
					</p>
				</>
			);
		}

		const root = document.getElementById('root');
		ReactDOM.render(<App />, root);
	</script>

APIからJSONデータを取得

const API = 'https://randomuser.me/api/?results=5&nat=us&inc=gender,name,email';

		function useUsers(){
			const [users, setUsers] = React.useState([]);

			React.useEffect(() => {
				(async () => {
					const response = await
					fetch(API).then(res => res.json());
					setUsers(response.results);
				})();
			},[]);

			return users;
		}


		function App(){
			const users = useUsers();
			return(
				<table>
					<thead>
						<tr>
							<td>Name</td>
							<td>Gender</td>
							<td>Email</td>
						</tr>
					</thead>
					<tbody>
						{users.map(user=>(
							<tr key={user.email}>
								<td>{user.name.title}. {user.name.first} {user.name.last}</td>
								<td>{user.gender}</td>
								<td>{user.email}</td>
							</tr>
						))}
					</tbody>
				</table>
			);
		}

【React 17.0.2】副作用(side effect)に使うuseEffect

副作用(side effect)とは、JSXを返却する処理およびその状態やイベントハンドラの定義以外の処理

非同期通信など
– DOMの存在を前提とした処理
– stateの更新
– 副作用はuseEffectの中で実行される必要がある
L 状態の更新により再レンダリングが発生するので、無限ループを避けるため、適切な場所で行う必要がある

function Component(){
	React.useEffect(()=>{

		someSideEffects();
	}, [])
}

useEffectはレンダリングが完了した後に実行される

function Component(){
	React.useEffect(()=>{

		new ExternalLibrary('.target-class');
	}, []);

	return (
		// jsx...
	);
}

状態の更新に関しても無限ループが発生しないようにする

function Component(){
	const[val, setVal] = React.useState(0);

	React.useEffect(() =>{
		setVal(123);
	}, []);
}

useEffect()はレンダリングが開始された直後に実行される
第二引数の配列を省略すると、コールバックはレンダリング後に毎回実行される

	React.useEffect(() =>{
		document.title = 'Hello world';
	}, []);
function Component(){
	const[val, setVal] = React.useState(0);

	React.useEffect(() =>{
		document.title = `Hello. ${firstName} ${lastName}`;
	}, [firstName, lastName]);
}
function App(){
			const[num, setNum] = React.useState(0);

			const handleClick = () => setNum(num + 1);

			return (
				<>
					<button onClick={handleClick}>{num}</button>
					{num % 2 === 0 && <Children />}
				</>
			);
		}

		function Children(){
			React.useEffect(() =>{
				console.log('hello');

				return() => console.log('bye');
			}, []);

			return <p>I am a child.</p>;
		}

### 非同期処理

function User({userId}){
	const [user, setUser] = React.useState(null);

	React.useEffect(() => {
		fetch('/api/users/${userId}')
			.then(res => res.json())
			.then(json => setUser(json));
	}, [userId]);

	return user ? <h1>Hello, {user.name}</h1> : <p>Loading...</p>
}

thenの代わりに async/await構文を用いる場合は注意が必要

– DOM操作の例

function ImageSlider({id, images}){
	const [swiper, setSwiper] = React.useState(null);

	React.useEffect(() => {
		const instance = new Swiper('#${id}', {
			// options...
		});
		setSwiper(instance);
	}, [id]);

	React.useEffect(() => {
		swiper.update();
	}, [images]);

	return (
		<div id={id} className="swiper-container">
			<div className="swiper-wrapper">
				{images.map(image => (
					<div key={image.id} className="swiper-slide">
						<img src={image.url} alt={image.title} />
					</div>
				))}
			</div>
		</div>
	);
}

非同期処理の場合は、TimeIntervalで実装できるのだろうか。。。
何か道が遠いな。

【React 17.0.2】フォームとイベントハンドラの扱い

state と onChange でstateを更新する
L useStateで初期値を設定している

		function App(){
			const [name, setName] = React.useState('Jhon');

			const handleChange = e => setName(e.target.value);

			return(
				<>
					<h1>Hello, {name}</h1>
					<input value={name} onChange={handleChange} />
				</>
			);
		}

textareaもinputと同じ

<textarea value={val} onChange={handleChange} />

– radioボタンを使う場合

		function App(){
			const [val, setVal] = React.useState('cat');

			const handleChange = e => setVal(e.target.value);

			return(
				<>
					<label>
						<input
							type="radio"
							value="cat"
							onChange={handleChange}
							checked={val == 'cat'}
						/>
						猫
					</label>
					<label>
						<input
							type="radio"
							value="dog"
							onChange={handleChange}
							checked={val == 'dog'}
						/>
						犬
					</label>
					<p>Your Choice: {val}</p>
				</>
			);
		}

– checkbox
L 配列の場合はuseStateのところで[”]とする
L stateは変更できず、val.push(e.target.value)とはできない

function App(){
			const [val, setVal] = React.useState(['js']);

			const handleChange = e => {
				if(val.includes(e.target.value)){
					setVal(val.filter(item => item !== e.target.value));
				} else {
					setVal([...val, e.target.value]);
				}
			};

			return(
				<>
					<label>
						<input
							type="checkbox"
							value="js"
							onChange={handleChange}
							checked={val.includes('js')}
						/>
						JavaScript
					</label>
					<label>
						<input
							type="checkbox"
							value="python"
							onChange={handleChange}
							checked={val.includes('python')}
						/>
						Python
					</label>
					<label>
						<input
							type="checkbox"
							value="java"
							onChange={handleChange}
							checked={val.includes('java')}
						/>
						Java
					</label>
					<p>Your Choice: {val.join(', ')}</p>
				</>
			);
		}

以下のように書くこともできる

			const initialVal = {js: true, python: false, java: false};
			const [val, setVal] = React.useState(initialVal);

			const handleChange = e => {
				const newVal = Object.assign({}, val, {
					[e.target.value] : !val[e.target.value]
				});
				setVal(newVal);
			};

– select box
L onChangeとするところなどほぼ同じですね

function App(){
			const [val, setVal] = React.useState('react');

			const handleChange = e => setVal(e.target.value);

			return(
				<>
					<select value={val} onChange={handleChange}>
						<option value="react">React</option>
						<option value="vue">Vue.js</option>
						<option value="angular">Angular</option>
					</select>
					<p>Your Choice:{val}</p>
				</>
			);
		}

– コンポーネントのイベント処理

function Tab({ onChange }){
			return (
				<ul>
					<li onClick={()=> onChange(1)}>React</li>
					<li onClick={()=> onChange(2)}>Vue.js</li>
					<li onClick={()=> onChange(3)}>Angular</li>
				</ul>
			);
		}

		function App(){
			const [tab, setTab] = React.useState(1);

			const handleChange = val => setTab(value);

			return(
				<>
					<Tab onChange={handleChange} />

					<div hidden={tab !== 1}>
						A JavaScript library for building user interfaces
					</div>
					<div hidden={tab !== 2}>
						The Progressive JavaScript Framework
					</div>
					<div hidden={tab !== 3}>
						One framework. Mobile &amp; desktop.
					</div>
				</>
			);
		}

React.useState()の使い方は何となくわかってきたのう

【React 17.0.2】属性(props)と状態(state)

Reactコンポーネントでも親子関係を持つと考えられる

<Parent>
	<ChildOne />
	<ChildTwo>
		<Baby />
	</ChildTwo>
</Parent>

props: 親から子へ渡される属性値 ※子コンポーネントで変更できない
state: 子の内部状態 ※親コンポーネントから参照・変更できない
L stateが更新されるとstateを持つコンポーネントが再レンダリングされる

### props
外部から渡される属性値は、propsと呼ぶ

<Component
	string="this is string"
	number={123}
	bool={true}
	array={[9,8,7]}
	object={{ hello: 'world'}}
	func = {a=> a*2}
	variable={something}
/>

<button
	id="the-button"
	className="button is-primary"
	onClick={handleClick}
>Button</button>
<label htmlFor="the-id">Label</label>

<input
	type="text"
	id="email"
	arial-labelledby="the-label"
	aria-required="true"
/>

### イベント処理
イベントの処理は別に書く

const handleClick = () => {
	
}

const button = <button onClick={handleClick}>Click me</button>;

### カスタム属性
独自要素には自由にpropsを定義できる

<Component customProp={something} />
<Layout
	header={<Header />}
	content={<Content />}
	footer={<Footer />}
/>

<Panel narrow>...</Panel>

### state
コンポーネント内部でデータを管理したい場合は、useState関数(state)を使う
  L useStateは更新されたデータを覚えていて、2回目以降はそちらを使う

		function CountButton(){
			const countState = React.useState(0);
 
			const count = countState[0]; // 状態
			const setCount = countState[1]; // 状態を更新するための関数

			const handleClick = () => setCount(count + 1);

			return <button onClick={handleClick}>{count}</button>;
		}

		const root = document.getElementById('root');


		ReactDOM.render(<CountButton />, root);

stateによる更新の影響範囲を確認

		function Parent(){
			const [num, setNum] = React.useState(0);

			const handleClick = () => setNum(num + 1);

			return(
				<>
					<p><button onClick={handleClick}>{num}</button></p>
					<p><Child num={num} /></p>
					<p><Child num={num} /></p>
				</>
			);
		}

		function Child({ num }){
			const [myNum, setMyNum] = React.useState(0);

			const handleClick = () => setMyNum(myNum + 1);

			return (
				<button className="child" onClick={handleClick}>
					{num + myNum}
				</button>
			);
		}

### フック
フックはReactの便利な機能を導入できるヘルパー関数
useStateはフックの一つ

なるほど、propsだけでなくuseStateを使って開発していくのね。
なんとなくイメージが沸きました。

Reactとは?何のメリットがあるのか?

– ReactとはDOM操作ライブラリでコンポーネント指向の開発スタイル
– ReactのDOM操作の特徴
  L 仮想DOM(Virtual DOM)
  L データとUIの同期
– Reactは具体的なDOM操作はコーディングせず、before afterのデータを更新する
– 仮想DOMの仕組みはReactのほか、Vue.jsでも採用されている
– props(属性)やstate(状態)といったReactが管理するデータを更新する

e.g. : JSでdom操作を書く場合

<div id="root"></div>
	<script>
		const element = document.createElement('p');
		element.id = 'the-text';
		element.className = 'text';
		element.innerText = 'Hello world';
		const root = document.getElementById('root');
		root.insertAdjacentElement('beforeend', element);
	</script>

入れ子(ul,li)の場合はこう書く
L React.createElementはReact要素を返却する
  L ReactDOM.renderは、第一引数がReact要素、第二引数がReact要素を内部に描画する親要素

		const element = React.createElement('ul', {className: 'list'}, [
				React.createElement('li', {className: 'list-item'}, ['one']),
				React.createElement('li', {className: 'list-item'}, ['two']),
				React.createElement('li', {className: 'list-item'}, ['three'])
			]);

### JSXはcreateElement
CDNからBabelを読み込んだ状態でscriptのtypeにbabelを指定すると実行される
JSXの構文は機能的にはcreateElementと同一

<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://unpkg.com/@babel/standalone/babel.min.js"></script>

	<script type="text/babel">
		const element = <p id="the-text" className="text">Hello React</p>;
		const root = document.getElementById('root');

		ReactDOM.render(element, root);
	</script>

React.createElementの属性値をpropsと言う。第三引数は第二引数のchildrenプロパティとして渡すこともできるので、以下のようにしても同じ。ただし、視認性の問題からタグで囲むことの方が多い

const element = <p id="the-text" className="text" children="JSX & babel" />;

JSXには、単一のルート要素を持つべきというルールがあるため、複数の要素を入れる場合は、親要素としてDivタグで囲むようにする
下記の場合はdiv要素がないとエラーで実行できない

const element = (
	<div>
		<article>...</article>
		<aside>...</aside>
	</div>
);

ただし、Fragment要素を利用した方がbetter

const element = (
	<React.Fragment>
		<article>...</article>
		<aside>...</aside>
	</React.Fragment>
);
const element = (
	<>
		<article>...</article>
		<aside>...</aside>
	</>
);

### 値の埋め込み

		const name = 'john';
		const element = <p id="the-text" className="text">{name}</p>;
		const members = [
			{name: 'John', instrument: 'guitar'},
			{name: 'Paul', instrument: 'bass'},
			{name: 'George', instrument: 'guitar'},
			{name: 'Ringo', instrument: 'drums'}
		];

		const element = (
			<ul>
				{members.map(member => (
					<li key={member.name}>
						{member.name} plays {member.instrument}
					</li>
					))}
			</ul>
		);

### コンポーネントを定義
Reactでは独自要素を作ることができ、独自要素はコンポーネントと呼ばれ、関数として定義します。
関数名はネイティブと区別するため必ず大文字で始め、React要素を返却する

function Sample(props){
			return <p id={props.id} className="text">{props.children}</p>
		}

		const root = document.getElementById('root');


		ReactDOM.render(<Sample id="the-text">Props Test</Sample>, root);

DOM操作のロジック部分と、表示部分を明示的に分けて記述してるのか
なるほど、良く考えられてて面白いですね。
最初ちょっと抵抗感ありましたけど、仕組みを理解すると分かりやすいです。

Ubuntu20.04でReactの環境構築

### ubuntuにnodejs, npm, yarnをインストール
$ cat /etc/os-release
NAME=”Ubuntu”
VERSION=”20.04.3 LTS (Focal Fossa)”
$ sudo apt update
$ sudo apt install nodejs
$ nodejs -v
v10.19.0
$ sudo apt install npm
$ npm -v
6.14.4
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add
$ echo “deb https://dl.yarnpkg.com/debian/ stable main” | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt install yarn
$ yarn -v
1.22.5
※yarnはrepositoryを入れないとinstallできないので注意が必要

### project作成
$ yarn create react-app hello
$ cd hello
$ yarn start

src/index.js
L 変更なし

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

src/App.js

function App() {
  return (
    <div className="App">
      <h1>Hello React!</h1>
    </div>
  );
}

export default App;

きゃー☺️

[React] 基礎

<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
ReactDOM.render(
  <h1>Hello world</h1>,
  document.getElementById('root')
);
</script>

jsxはjsの拡張構文でタグを含められる
コンパイルすれば、babelは使用しなくて良い

<body>
	<div id="content">
		
	</div>
</body>
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
class ComponentBox extends React.Component{
	render(){
		return (
			<div className="ComponentBox">
			Component Box
			</div>
		);
	}
};
ReactDOM.render(
	<ComponentBox/>,
	document.getElementById('content')
);
</script>
</html>

<script type="text/babel">
class Layout extends React.Component{
	render(){
		return (
			<h1>welcome!</h1>
		);
	}
};
const app = document.getElementById('app');
ReactDOM.render(
	<Layout/>, app
);
</script>

こういうこともできる

class Layout extends React.Component{
	constructor(){
		super();
		this.name = "Hpscript";
	}
	render(){
		return (
			<h1>It's {this.name}</h1>
		);
	}
};

なるほどー

Reduxを始める

まずReact.js&webpack&babelから
#################
$ npm init
$ npm install –save-dev webpack webpack-cli webpack-dev-server
$ npm install –save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
$ npm install –save-dev react react-dom
$ npm install –save-dev @babel/plugin-proposal-class-properties

.babelrc作成

{
	"plugins": [
		"@babel/plugin-proposal-class-properties"
	]
}

webpack.config.jsの作成
srcディレクトリの作成
#################

ここから、Reduxを使い始めたい。
公式に沿ってやっていく。(https://redux.js.org/introduction/getting-started)

npm i react-redux

$ npm install --save-dev react-redux
$ npm install --save-dev redux

basic example

import { createStore } from 'redux'

function counter(state = 0, action){
	swtich(action.type){
		case 'INCREMENT':
			return state + 1
		case 'DECREMENT':
			return state - 1
		default:
			return state
	}	
}

let store = createStore(counter)

store.subscribe(() => console.log(store.getState()))

store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'INCREMENT' })

createStoreで状態遷移を管理?
全体の動きがよくわからんな。。