【React】react-hook-formのバリデーション

import './App.css';
import { useForm } from 'react-hook-form';

function App() {
  const { register, 
  handleSubmit,
  formState: {errors},
  } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <div className="App">
      <h1>login</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="email">Email</label>
          <input id="email" {...register('email', { required: true })} />
          {errors.email && <div>入力が必須の項目です</div>}
        </div>
        <div>
          <label htmlFor="password">パスワード</label>
          <input
            id="password"
            {...register('password')} type="password" 
          />
        </div>
        <div>
          <button type="submit">Login</button>
        </div>
      </form>
    </div>
  );
}

複数のエラーメッセージ

function App() {
  const { register, 
  handleSubmit,
  formState: {errors},
  } = useForm({
    criteriaMode: 'all',
  });

<label htmlFor="password">パスワード</label>
          <input
            id="password"
            {...register('password', {
              required: {
                value: true,
                message: '入力が必須の項目です。',
              },
              pattern: {
                value: /^[A-Za-z]+$/,
                message: 'アルファベットのみ入力してください。'
              },
              minLength: {
                value: 8,
                message: '8文字以上入力してください。'
              }
            })} type="password" 
          />
          {errors.password?.type === 'required' && (<div>{errors.password.types.required}</div>)}
          {errors.password?.type === 'pattern' && (<div>{errors.password.types.pattern}</div>)}
          {errors.password?.type === 'minLength' && (<div>8文字以上入力してください。</div>)}
        </div>

バリデーションのタイミングは制御できる
reValidateMode: ‘onSubmit’,
mode: ‘onChange’,

なるほど、何となくわかってきました。
さぁ、実装していきますか…

【React】react-hook-formを使ってみる

$ yarn add react-hook-form

react-hook-formを使わずに書いた場合 1

import './App.css';
import { useState } from 'react';

function App() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({
      email,
      password,
    });
  };

  const handleChangeEmail = (e) => {
    setEmail(e.target.value);
  };
  const handleChangePassword = (e) => {
    setPassword(e.target.value);
  };
  return (
    <div className="App">
      <h1>login</h1>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="email">Email</label>
          <input id="email" name="email" value={email} onChange={handleChangeEmail} />
        </div>
        <div>
          <label htmlFor="password">パスワード</label>
          <input
            id="password"
            name="password"
            value={password}
            onChange={handleChangePassword}
            type="password"
          />
        </div>
        <div>
          <button type="submit">Login</button>
        </div>
      </form>
    </div>
  );
}

export default App;

react-hook-formを使わずに書いた場合 2

import './App.css';
import { useRef } from 'react';
import 

function App() {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({
      email: emailRef.current.value,
      password: passwordRef.current.value,
    });
  };

  // const handleChangeEmail = (e) => {
  //   setEmail(e.target.value);
  // };
  // const handleChangePassword = (e) => {
  //   setPassword(e.target.value);
  // };
  return (
    <div className="App">
      <h1>login</h1>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="email">Email</label>
          <input id="email" name="email" ref={emailRef} />
        </div>
        <div>
          <label htmlFor="password">パスワード</label>
          <input
            id="password"
            name="password"
            ref={passwordRef} type="password" 
          />
        </div>
        <div>
          <button type="submit">Login</button>
        </div>
      </form>
    </div>
  );
}

export default App;

### react-hook-formを使った場合

import './App.css';
import { useForm } from 'react-hook-form';

function App() {
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <div className="App">
      <h1>login</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="email">Email</label>
          <input id="email" {...register('email')} />
        </div>
        <div>
          <label htmlFor="password">パスワード</label>
          <input
            id="password"
            {...register('password')} type="password" 
          />
        </div>
        <div>
          <button type="submit">Login</button>
        </div>
      </form>
    </div>
  );
}

export default App;

registerに入っている値

const { name, ref, onChange, onBlur } = register('email');
//略
<input
  id="email"
  name={name}
  onChange={onChange}
  onBlur={onBlur}
  ref={ref}
/>

Reactでカウンターを作る

import React, { Component } from 'react';

const App = () => {
  return <Counter />
}

class Counter extends Component
{
  constructor(props)
  {
    super(props);
    this.state = {
      value : 0
    }
  }

  onIncrement = () => {
    this.setState({ value : this.state.value + 1});
  }

  onDecrement = () => {
    this.setState({ value : this.state.value - 1});
  }

  render()
  {
    return (
      <div>
        <div>
          Count value: {this.state.value}
        </div>
          
        <div>
        <button type="button" onClick={this.onIncrement}>+</button>
        <button type="button" onClick={this.onDecrement}>-</button>
        </div>
      </div>
    );
  }
}
export default App;

なるほど、面白いです^^

Reactで作ったコンポーネントをWebページに埋め込む

まず、Rectで作った簡単なjsファイル

App.js

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

const App = () => {
  
  return (
    <div>
      <h1>hello world { 1 + 2}</h1>
    </div>
  )
  
}

export default App;

ファイルをbuidします。

$ npm run build

すると、buid/static/js にファイルが生成されているので、これを埋め込みます。
ここでは main.50873ce4.jsとなっていました。

<body>
    <h1>hello world!</h1>
    <div id="root"></div>
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <script src="main.js" crossorigin></script>
</body>

なるほど、フッターにインラインで記述するのではなく、Node JSで開発して、それをminifyしてコンポーネントとして埋め込むのね。
何となくReactの使い方は理解しました。

Reactの基礎 [環境構築]

$ npm -v
10.9.2
$ node -v
v22.13.1
$ yarn –version
bash: yarn: command not found

$ sudo npm install –global yarn
$ yarn –version
1.22.22

$ yarn create react-app helloworld
$ cd helloworld
$ ls
node_modules package.json public README.md src yarn.lock

$ yarn start

src/App.js

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

修正する

import React from 'react';

const App = () => {
  return (
    <div>
      <h1>hello world</h1>
    </div>
  )
}

export default App;

ちょっと思い出してきました。

Reactの環境構築 & AntDesign

$ node –version
v16.13.2
$ npx create-react-app react_app
$ yarn start
http://192.168.56.10:3000/
=> 問題なければstopする

ant designもインストール
$ npm install antd
$ npm audit fix –force

srcディレクトリ内で作業
App.js

function App(){
  return (
      <div className='App'>
        <header className='App-header'>
        </header>
      </div>
    )
}
import {
	BellFilled,
	CaretDownOutlined,
	FormOutlined,
	UserOutlined,
} from '@ant-design/icons/lib/icons';
import { Avatar, Col, Input, Row, Typography } from 'antd';

function App(){
  return (
      <div className='App'>
        <header className='App-header'>
        	<div>
        		<Title>Hpscript</Title>
        		<div>
        			<CaretDownOutlined />
        			<Input placeholder='キーワードを入力' />
        		</div>
        		<div>
        			<div>
        				<FormOutlined />
        				<Text>
        					投稿する
        				</Text>
        			</div>
        			<div>
        				<BellFilled />
        			</div>
        			<Avatar size='large' icon={<UserOutlined />} />
        		</div>
        	</div>
        </header>
      </div>
    );
}

export default App;

src/App.js
Line 14:12: ‘Title’ is not defined react/jsx-no-undef
Line 22:14: ‘Text’ is not defined react/jsx-no-undef

import { Breadcrumb } from 'antd';
import React from 'react';

const App: React.FC = () => (
	<Breadcrumb>
	    <Breadcrumb.Item>Topページ</Breadcrumb.Item>
	    <Breadcrumb.Item>
	      <a href="">ページタイトルA</a>
	    </Breadcrumb.Item>
	    <Breadcrumb.Item>
	      <a href="">ページタイトルB</a>
	    </Breadcrumb.Item>
	    <Breadcrumb.Item>ページタイトルC</Breadcrumb.Item>
	</Breadcrumb>
);

export default App;

Datepicker

import React from 'react';
import { DatePicker } from 'antd';

const App = () => {
	return <DatePicker />;
}

export default App;

First Example

import React from 'react';
import { Button, Space, DatePicker, version } from 'antd';

const App = () => (
	<div style={{padding: '0 24px '}}>
	<h1>antd version: {version}</h1>
	<Space>
		<DatePicker />
		<Button type="primary">Primary Button</Button>
	</Space>
	</div>
)

export default App;

getting start

import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { DatePicker, message } from 'antd';
import './index.css';

const App = () => {
  const [date, setDate] = useState(null);
  const [messageApi, contextHolder] = message.useMessage();
  const handleChange = (value) => {
    messageApi.info(`Selected Date: ${value ? value.format('YYYY-MM-DD') : 'None'}`);
    setDate(value);
  };
  return (
    <div style={{ width: 400, margin: '100px auto' }}>
      <DatePicker onChange={handleChange} />
      <div style={{ marginTop: 16 }}>
        Selected Date: {date ? date.format('YYYY-MM-DD') : 'None'}
      </div>
      {contextHolder}
    </div>
  );
};

createRoot(document.getElementById('root')).render(<App />);
export default App;
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { DatePicker, message, Alert } from 'antd';
import './index.css';

const App = () => {
  const [date, setDate] = useState(null);
  const [messageApi, contextHolder] = message.useMessage();
  const handleChange = (value) => {
    messageApi.info(`Selected Date: ${value ? value.format('YYYY-MM-DD') : 'None'}`);
    setDate(value);
  };
  return (
    <div style={{ width: 400, margin: '100px auto' }}>
      <DatePicker onChange={value => this.handleChange(value)} />
      <div style={{ marginTop: 20 }}>
      	<Alert message="Selected Date" description={date ? date.format('YYYY-MM-DD') : 'None'} />
      </div>
      {contextHolder}
    </div>
  );
};

createRoot(document.getElementById('root')).render(<App />);
export default App;
import { ConfigProvider } from 'antd';
import React from 'react';


const App: React.FC = () => (
	<ConfigProvider theme={{ token: { colorPrimary: '#00b96b'} }}>
		<MyApp />
	</ConfigProvider>
);

export default App;

[Ethereum] Reactでdappを作りたい

$ sudo npm install -g create-react-app
$ create-react-app lottery-react
$ cd lottery-react
$ npm run start

http://192.168.34.10:3000/

$ sudo npm install -g yarn
$ yarn -v
1.22.5
$ npm install –save web3@1.0.0-beta.26

$ cd ..
$ mkdir lottery
$ cd lottery
$ sudo npm install solc@0.4.17
$ npm install –save mocha ganache-cli web3@1.0.0-beta.26
$ npm install –save truffle-hdwallet-provider@0.0.3

contract/Lottery.sol

pragma solidity ^0.4.17;

contract Lottery {
	address public manager;
	address[] public players;

	function Lottery () public {
		manager = msg.sender;
	}

	function enter() public payable {
		require(msg.value > .01 ether);

		players.push(msg.sender);
	}

	function random() private view return (uint) {
		return uint(keccak256(block.difficulty, now, players));
	}

	function pickWinner() public restricted {
		uint index = random() % players.length;
		players[index].transfer(this.balance);
		players = new address[](0);
	}

	modifier restricted() {
		require(msg.sender == manager);
		_;
	}

	function getPlayers() public view returns(address[]){
		return players;
	}
}

compiler.js

const path = require('path');
const fs = require('fs');
const solc = require('solc');

const IndexPath = path.resolve(__dirname,'contracts', 'Lottery.sol');
const source = fs.readFileSync(IndexPath, 'utf8');

console.log(solc.compile(source,1));

ABIとは、Application Binary Interface

lottery.js

import web3 from './web3';

const address = '0xaC24CD668b4A6d2935b09596F1558c1E305F62F1';


const abi = [{
    "constant": true,
    "inputs": [],
    "name": "manager",
    "outputs": [{
        "name": "",
        "type": "address"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "pickWinner",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getPlayers",
    "outputs": [{
        "name": "",
        "type": "address[]"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "enter",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
}, {
    "constant": true,
    "inputs": [{
        "name": "",
        "type": "uint256"
    }],
    "name": "players",
    "outputs": [{
        "name": "",
        "type": "address"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
}];


export default new web3.eth.Contract(abi, address);

web3.js

import Web3 from 'web3';

const web3 = new Web3(window.web3.currentProvider);

export default web3;

App.js

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

class App extends Component {
  state = {
    manager: '',
    player: [],
    balance: '',
    value: '',
    messaget: ''
  };

  async componentDidMount() {
    const manager = await lottery.methods.manager().call();
    const players = await lottery.methods.getPlayers().call();
    const balance = await web3.eth.getBalance(lottery.opetions.address);

    this.setState({manager, players, balance});
  }

  onSubmit = async(event) => {
    event.preventDefault();

    const accounts = await web3.eth.getAccounts();

    this.setState({message: 'Waiting on transaction success...'});

    await lottery.methods.enter().send({
      from: accounts[0],
      value: web3.utils.toWei(this.state.value, 'ether')
    });

    this.setState({message: 'you have been entered!'});
  };

  onClick = async() => {
    const accounts = await web3.eth.getAccounts();

    this.setState({message: "waiting on transaction success..."});

    await lottery.methods.pickWinner().send({
      from: accounts[0]
    });

    this.setState({message: 'A winner has been picked'});
  };

  render() {
    return (
    <div>
      <h2>Lottery Contract</h2>
      <p>
        This contract is managed by {this.state.manager}.
        There are currently {this.state.players.length} peple entered,
        competing to win {web3.utils.fromWei(this.state.balance, 'ether')}
      </p>
      <hr />

        <form onSubmit={this.onSubmit}>
            <h4>Want to try your luck?</h4>
            <div>
              <label>Amount of ether to enter</label>
              <input
              value = {this.state.value}
                onChange={event => this.setState({ value: event.target.value })}
              />
            </div>
            <button>Enter</button>
        </form>

        <hr />

        <h4>Ready to pick a winner?</h4>
        <button onClick={this.onClick}>Pick a winner!</button>

        <hr />
        <h1>{this.state.message}</h1>
      </div>
  );
  }
}

export default App;

なるほど、全体の流れはわかった

[Python3 x React.js] S3にアップロードしたjsonファイルをリアルタイムで読み込む

pythonでS3にアップロードします
 L S3 full accessのcredentialは別ファイルからimportにします。

import boto3
import json
import credentials

str = {
	"speech":"最高"
}

with open("sample.json", "w") as f:
	json.dump(str, f, ensure_ascii=False)

s3 = boto3.client('s3', aws_access_key_id=credentials.Accesskey, aws_secret_access_key= credentials.Secretkey, region_name=credentials.Region)
 
filename = "sample.json"
bucket_name = "hogehoge"
 
s3.upload_file(filename,bucket_name,filename, ExtraArgs={'ContentType': "application/json",'ACL':'public-read'})
print("upload {0}".format(filename))

React.jsでJsonをsetIntervalで読み込みます

function App(){
			const [data, setData] = React.useState([]);

			React.useEffect(() => {
				const fetchData = async() => {
				fetch("https://hoge.s3.ap-northeast-1.amazonaws.com/sample.json")
					.then((res) => res.json())
					.then((data) =>{
						setData(data);
				});
				}

				const id = setInterval(() => {
				    fetchData();
				 }, 2000);
				
			}, []);

			console.log(data);

			return(
				<h1>コメント:{data.speech}</h1>
			);
		}

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

pythonでuploadしたワードがブラウザの更新なくHTML側に表示されます。
OK、後はUIを作っていく

【React 17.0.2】jsonからデータを取得する

まずjsonデータを作成します。適当に株価で作ります。

{
	"stocks": [
		{"id": 4563, "name":"アンジェス", "price": 780},
		{"id": 9318, "name":"アジア開発キャピタル", "price": 9},
		{"id": 8256, "name":"プロルート丸光", "price": 283},
		{"id": 4755, "name":"楽天グループ", "price": 1132},
		{"id": 9984, "name":"ソフトバンクグループ", "price": 7179}
	]
}

### ReactのClassで書く場合

<script type="text/babel">
		class App extends React.Component{
			constructor(props){
				super(props)
				this.state = {
					isLoaded: false,
					error: null,
					stocks: []
				}
			}

			componentDidMount(){
				fetch("http://192.168.34.10:8000/data.json")
				.then(res=> res.json())
				.then(
					(result) => {
						this.setState({
							isLoaded: true,
							stocks: result.stocks
						});
					},
					(error) => {
					this.setState({
						isLoaded: true,
						error: error
					});
				})
			}

			render(){
				if(this.state.error){
					return <div>Error: {this.state.error.message}</div>
				} else if(!this.state.isLoaded) {
					return <div>Loading...</div>
				} else {
					return (
						<ul>
							{this.state.stocks.map( stock =>(
									<li key={stock.id}>
										{stock.name} {stock.price}
									</li>
								))}
						</ul>
					);
				}
			}

		}

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

### function(関数)で書く場合

function App(){
			const [data, setData] = React.useState([]);

			React.useEffect(() => {
				fetch("http://192.168.34.10:8000/data.json")
					.then((resoponse) => resoponse.json())
					.then((data) =>{
						setData(data.stocks);
				});
			}, []);

			return(
				<ul>
					{data.map( stock =>(
							<li key={stock.id}>
								{stock.name} {stock.price}
							</li>
						))}
				</ul>
			);
		}

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

結果は同じですが、関数型の方が遥かに書きやすいですね。

これをbitcoinで実装する

function App(){
			const [data, setData] = React.useState([]);

			React.useEffect(() => {
				fetch("https://blockchain.info/ticker")
					.then((resoponse) => resoponse.json())
					.then((data) =>{
						setData(data.JPY);
				});
			}, []);

			console.log(data);

			return(
				<h1>ビットコイン価格(JPY):{data.last}</h1>
			);
		}

settime intervalでやりたい。

【React 17.0.2】Hooks

### useState
React本体に関数コンポーネント専用の保存領域を作成してもらい、そこにあるデータを読み書きできる

			const[count, setCount] = useState(0);
			return (
				<div onClick={()=>setCount(count+1)}>{count}</div>
			);

複数の値を扱うときは、複数のuseStateを使う

			const[count, setCount] = useState(0);
			const[isRed, setIsRed] = useState(false);

			return (
				<div 
					style={isRed? {color: 'red'}: null}
					onClick={() => {
						setCount(count+1);
						setIsRed((count+1)%3 === 0);
				}}
				>
			{count}
			</div>
			);

hooksをifやfor文の中で使用してはいけない

### useMemo
useMemoは単純に値を保存するためのhook

			const Calculator = () => {
				const calcResult = useMemo(() => expensiveFunc(), []);

				return <div>{calcResult}</div>
			}

### useCallback
useCallback(func, []) と省略した書き方ができる

			const Button = React.memo((props) => {
				return <button onClick={props.onClick}>Click Me</button>
			});

			const App = () => {
				const onClick = useCallback(() => alert('Hi!'), []);
				return <Button onClick={onClick}/>
			}

### useEffect
コンポーネントのレンダリング後に実行される

			const Message = () => {

				const elem = document.querySelector('#message');
				console.log(elem);

				useEffect(() => {
					const elem = document.querySelector('#message');
					console.log(elem);
					elem.innerText = "Hello!"
				});

				return <div id="message"></div>
			}

### useRef
useStateの参照版。useRefを使うことでReact本体側に値を保存でき、オブジェクトでラップされる

			const Message = () => {

				const divRef = useRef(null);

				useEffect(() => {
					divRef.current.innerText = 'Hello!';
				}, []);

				return <div ref={divRef}></div>
			}

実装したい内容に応じて、Hookを使い分けるってことか。