RPM Tips Stack
随時更新
- 強制インストール(ダウングレード防止)
rpm -Uvh --replacepkg --replacefiles xxx.rpm
Doc: https://kanaxx.hatenablog.jp/entry/hatena-entry-update
注意点
<updated>
タグを入れないと、更新日がスクリプト実行日に変わってしまう。作成日も一緒に変わってしまう。<category>
タグを常に入れて送らないとカテゴリーがクリアされてしまう。from datetime import datetime from xml.sax.saxutils import escape __BLOG_ENTRY_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app"> <title>{title}</title> <author><name>{author}</name></author> <content type="text/x-markdown">{content}</content> <updated>{update_time}</updated> <category term="{category}" /> <app:control> <app:draft>{draft}</app:draft> </app:control> </entry>""" ENTRY_DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" def resolve_entry_current_time() -> str: return datetime.now().strftime(ENTRY_DATE_TIME_FORMAT) def __replace_xml_escape(content: str) -> str: return escape(content) # escape: <, &, >, def build_hatena_blog_entry_xml_body(hatena_id: str, title: str, category: str, content: str, is_draft: bool = True) -> str: entry_xml = __BLOG_ENTRY_TEMPLATE.format( title=title, author=hatena_id, content=__replace_xml_escape(content), update_time=resolve_entry_current_time(), category=category, draft='yes' if is_draft else 'no' # yes or no ) return entry_xml
import base64 import hashlib import random from datetime import datetime class HatenaBlogApiExecutor: def __init__(self, blog_config: BlogConfig): self.__blog_conf = blog_config def build_request_header(self): def __build_wsse(blog_config: BlogConfig): user_name = blog_config.hatena_id api_key = blog_config.api_key created_time = datetime.now().isoformat() + "Z" b_nonce = hashlib.sha1(str(random.random()).encode()).digest() b_password_digest = hashlib.sha1(b_nonce + created_time.encode() + api_key.encode()).digest() wsse = f'UsernameToken Username={user_name}, ' + f'PasswordDigest={base64.b64encode(b_password_digest).decode()}, ' + f'Nonce={base64.b64encode(b_nonce).decode()}, ' + f'Created={created_time}' return wsse return { 'X-WSSE': __build_wsse(self.__blog_conf) }
import xml.etree.ElementTree as ET from datetime import datetime from typing import List, Optional # for debug def print_xml_children(root: ET.Element): """ for debug """ for child in root: print(child.tag) def __get_tag_head(root: ET.Element, root_tag: str = 'feed') -> str: tag_head = root.tag[:-len(root_tag)] # tag example: {http://www.w3.org/2005/Atom}feed return tag_head def get_next_page_url(xml_string: str) -> Optional[str]: url = None root = ET.fromstring(xml_string) for link in root.iter(__get_tag_head(root) + 'link'): if link.attrib['rel'] == 'next': url = link.attrib['href'] break return url def parse_blog_entry_xml(xml_string_opt: str) -> Optional[BlogEntry]: if xml_string_opt is None: return None root = ET.fromstring(xml_string_opt) tag_head = __get_tag_head(root, 'entry') return __parse_blog_entry_xml(root, tag_head, []) def __parse_blog_entry_xml(entry_node: ET.Element, tag_head: str, exclude_ids: List[str]) -> Optional[BlogEntry]: # id example: tag:blog.hatena.ne.jp,2013:blog-Sympathia-17680117126980108518-13574176438048806685 # entry id is last sequence entry_id = entry_node.find(tag_head + 'id').text.rsplit('-', 1)[1] if entry_id in exclude_ids: return None title = entry_node.find(tag_head + 'title').text content = '' for cont in entry_node.iter(tag_head + 'content'): if cont.attrib['type'] == 'text/x-markdown': content = cont.text break updated_opt = entry_node.find(tag_head + 'updated') last_update_time = None if updated_opt is not None: # format: 2013-09-02T11:28:23+09:00 last_update_time = datetime.strptime(updated_opt.text, "%Y-%m-%dT%H:%M:%S%z") app_edited_opt = entry_node.find('{http://www.w3.org/2007/app}edited') # app:edited if app_edited_opt is not None: # format: 2013-09-02T11:28:23+09:00 app_edited_time = datetime.strptime(app_edited_opt.text, "%Y-%m-%dT%H:%M:%S%z") if last_update_time < app_edited_time: last_update_time = app_edited_time url_link = '' for link in entry_node.iter(tag_head + 'link'): if link.attrib['rel'] == 'alternate': url_link = link.attrib['href'] break categories = [] for category in entry_node.iter(tag_head + 'category'): categories.append(category.attrib['term']) return BlogEntry(entry_id, title, content, url_link, last_update_time, categories)
マイクロサービス
SOA(Service Oriented Architecture): サービス指向設計
システムの機能単位で決めていく設計(業務分析必須)
|SOA | マイクロサービス |
---|---|
アーキテクチャ | リソース共有(同じサーバ)|独立したサービス(サーバも複数) |
コンポーネント共有 | 共有する|共有しない |
サービス粒度 | 比較的大きい|非常に小さい |
サービス間通信 | ESB(Enterprise Service Bus)|メッセージブローカー |
通信プロトコル | SOAP|REST/gRPC |
- | 1対1 | 1対多 |
---|---|---|
同期通信 | REST/gRPC | 無し |
非同期通信 | バッチ | メッセージブローカー/DB共有 |
参考
- | 利点 | 欠点 |
---|---|---|
キュー方式(Pull方式) | 処理量が多くても対応可能(=バッチ処理に対応可能) | 多少タイムラグが出る |
Pub/Sub方式(Push方式) | 軽い処理に向く(リアルタイムに近づけられる) | Consumer側(Sub側)のリソースを考慮する必要がある |
サービス分割により、
サービス同士が接続しあい、複雑なネットワークを構成している場合に、局所的な障害がシステム全体に波及させない仕組み。 ブレーカー(Breaker)を落とすようにネットワーク上の回路(Circuit)を遮断する
カスケード障害(ネットワークの一部の障害が要因となり,連鎖的に障害がシステム全体へ波及する現象)を起さないための手段
参考
中央にサービスレジストリというアクセス先をまとめるものを持つ。インスタンス(サーバ) が立ち上がった時にサービスレジストリに自身のIPを登録。クライアントはサービスレジストリに対して、アクセスしたいサービスを元に問い合わせし、IPアドレスをもらってそれでサーバにアクセスする
参考
課題
リバースプロキシの役割を担い、リクエストを各APIに振り分ける。公開点を限定するためセキュリティ向上にも繋がる
代表:Kong
課題
ある1つのサービスが中心となってデータを収集・結合する。
ただし、分かりやすい反面、以下の欠点有
コマンドクエリ責任分離(Command & Query Responsibility Segregation)
OAuth/OpenID Connect:認可の仕組み
OAuthはサービスが持つ各APIにアクセス許可、OpenID Connectはユーザ情報特化のイメージ。
OpenID Connect = OAuth 2.0 + Identity Layer(ID Token + UserInfo API)
OpenID Connect の場合はアクセストークンに加えIDトークン(=JWT)も受け取る
参考:
マイクロサービスの場合
認可サーバを用意して、
課題
参考:
デプロイ方法 | 概要 | アーティファクトに含むもの | 利点 | 欠点 |
---|---|---|---|---|
アーティファクト配置 | 資材を物理マシンや仮想マシンへ配置する | アプリ/データ | アプリ開発チームにとっては単純 | インフラ管理が大変 |
VMイメージ | 資材配置が終わった仮想マシンのイメージ作成/展開/起動 | アプリ/データ/ミドル/ランタイム/OS | インフラがイミュータブルにできる | 仮想イメージを使ってサーバを作るのは重い |
コンテナ | 資材配置が終わったコンテナを作成/起動 | アプリ/データ/ミドル/ランタイム | OS部分が共通化でき、軽くなる | ミドル/ランタイムが新しい物が出ると作り直す必要有 |
サーバーレス | クラウド事業者が提供する環境に直接資材を投入 | アプリ/データ | 管理範囲を減らせる(クラウド事業者に委譲) | |
サーバーレス | クラウド事業者が提供する環境に直接資材を投入 | アプリ/データ | 管理範囲を減らせる(クラウド事業者に委譲) |
下の物ほど、調整事を減らせ、リリースまでのリードタイムを減らせる
方法 | デプロイ先 | ダウンタイム | 特徴 |
---|---|---|---|
サービス停止&デプロイ | 稼働中サーバ | 閉塞期間 | ユーザが利用できない時間が発生する |
ブルー/グリーンデプロイ | 新規サーバ | ほぼゼロ | サービス停止せず、リリース後は新しい環境を使う。切り戻しが容易 |
イミュータブルデプロイ | 新規サーバ | ほぼゼロ | 上記と同じだが、元のサーバは削除する。切り戻しする際は、古いバージョンをリリースしなおす |
ローリングデプロイ | 新規サーバ | ゼロ | 数台ずつイミュータブルデプロイ |
横断的機能(例:サーキットブレ―カー、認証認可、通信暗号化、モニタリング)はサービスとは別に外付け機能として実装 ⇒ サービスメッシュ
マイクロサービスごとにプロキシを配置し、プロキシを経由して他のサービスと通信させることでサービスの依存関係やトラフィックを制御する仕組み
参考:
代表:
課題:
対策
代表
目的:リリースの頻度を上げる
CI(Continuous Integration):実装 > コミット > マージ > 自動ビルド > 自動テスト CD(Continuous Delivery): 上記 > デプロイ > リリース
課題:各サービスの正常動作を判断する必要がある (例:GET /health /hc /healthz)
対策:稼働状態を返す専用のAPIを公開する
課題:障害発生箇所の分析や障害原因の特定が難しい
対策:複雑なシステムを横断的に監視できるようにする
手段 | 概要 | 補足 | ツール |
---|---|---|---|
ログ収集 | 各サービスで発生するイベントの記録 | ログは1箇所に収集/集約。ログ送信はサービスが多い場合バッファリングも検討 | Elasticsearch Kibana/Fluentd |
メトリクス | 各サービスの定量数値情報の記録(増減の確認) | モニタリングとアラートのために収集。収集自体はログ収集と同じやり方 | 〃 |
分散トレーシング | サービスが呼ばれた順序やその時の状態を分析/可視化 | Trace ID/Span ID/Parent Span IDを保存 | Zipkin/Jaeger/AWSはX-Ray |
利用用途(要件)に合わせたツールを選定
段階的に進めるべき。
ポイント
目的をはっきりさせる
新規事業よりもある程度成熟しドメイン境界がはっきりした事業に向く(モノリシックでは効率が悪くなってきたときにマイクロサービスへの移行を考えるもの)
レビンの変革プロセス
ジョン・コッターのリーダーシップ論に学ぶ『変革時代で生き残るためには?』(変革へ至るための8つプロセス)
方法 | メリット | デメリット |
---|---|---|
プロキシを利用した分割 | モノリスに対する修正が減らせる | URL単位である程度分割できないと利用できない |
I/F定義を利用した分割 | 複雑化したコードでも分割できる | モノリスに対する修正要、リリース調整要 |
データベースの分割
物理分割:DBサーバを分割
論理分割:DBサーバは共有し、スキーマを分割
方法 | メリット | デメリット |
---|---|---|
データから分割 | データに対して分割に問題がないか確認しやすい | マイクロサービスの効果を体感しづらい |
アプリから分割 | マイクロサービス化の開発プロセスやメリットを体感しやすい | 意識的にデータ分割まで行わないと潜在リスクを抱えたままになる |
アプリとデータ同時分割 | マイクロサービスとしての理想形にいきなり到達可能 | 修正コード量が増えるためマイクロサービスの効果を体感するまで時間を要する |
直感
※プロダクトの特性も考慮して考える必要はあるはず
ルール/仕組みがある上で初めて自由が許される世界
N+1問題
その他参考情報 『Microservice Patterns』 まとめ
目的を一言で表現するならば
特徴
以下、REST API の課題を解消する狙い
(GraphQLドキュメントより翻訳)
GraphQLはRESTに代わる、より効率的で強力かつ柔軟な代替手段を提供する新しいAPI標準です。GraphQLは、そのコアで、クライアントがAPIから必要なデータを正確に指定できる宣言型データフェッチを可能にします。固定データ構造を返す複数のエンドポイントの代わりに、GraphQLサーバーは単一のエンドポイントのみを公開し、クライアントが要求したデータで正確に応答します。
REST API:リソースベース
GraphQL:ユースケースベース(でqueryを定義するのが良いとされている)
間違いなく言えるのは、管理しているデータが網目状に関連を持ち、(ユースケース的に)色々なルートでその関連を辿る必要がある場合
分かりやすい例:
これをREST APIで対応しようとすると
となり、RESTの悪い点を踏む
「GraphQLの方が色んな面で優れているため、RESTよりGraphQLを使った方が良い」とはならないケースはあるはず。
REST APIの優位性は、シンプルさと統一性(HTTPメソッドとリソースに対応するURI)。
GraphQLサーバ と REST API サーバ の利用者目線(利用者≠フロントエンド。単純にAPIを打つ人という位置づけ)で考えた時に、
GraphQL は、必ずクエリを指定し、対象のリソースや欲しい情報を指定しなければならない。つまり、利用者にその負担を強いる(REST API も更新系は概ねリクエストボディで情報を送る必要があるため手間はそこまで変わらないかもしれないが、参照系はそうでもない)。
複数のサービスから、1リソースの情報を取得しようとした時にどちらが扱いやすいかを考えるとREST API に軍配が上がるだろう(GraphQLだと、複数サービスで同じユースケース(とあるリソース情報を取得する)があれば、複数サービスで同じクエリを持つことになる)。
ただ、RESTでは要求に柔軟に対応することも、リソース間の関連が多いようなデータを扱うのは不得意なため
は、REST API の方が良いのではないか。DBで管理するマスターデータ、トランザクションデータのうちの、マスターデータにあたるような物は、データ構造次第ではREST APIの方が扱いやすい可能性があると思われる。
GraphQL拡張仕様
https://relay.dev/docs/guides/graphql-server-specification/
Todo
Client
Client と Server どちらも用意できるのが Apollo
Apollo Server は、REST APIをデータソースとすることもできるため、Apollo Serverをプロキシとし、GraphQLのインターフェースを提供することができる。つまり、段階的移行が可能である。
Apollo ServerとPrismaではじめるGraphQL API開発入門
Apollo なら爆速で GraphQL サーバーと GraphQL クライアントアプリが作れる
GraphQL で N+1 問題を解決する 4 つのアプローチ
doc
important
概要/特徴
設計
実装
その他
対応言語
副作用:描画の一部ではない処理。UI構築に関する以外の処理
レデューサー:同じ引数の場合、必ず同じ戻り値を返さなければならない
メモ化
※なんでもかんでもメモ化すればいい訳ではない。メモ化にもコストはかかる
Reactは初めからパフォーマンスを年頭に置いて設計されている。
再レンダリング戦略
ref:
export const CheckBox: VFC = () => { const [checked, setChecked] = useState<boolean>(false); useEffect(() => { alert(`checked: ${checked.toString()}`); }); // alert(`checked: ${checked.toString()}`); // OK押下されるまで↓の処理が実行されない return ( <> <input type="checkbox" checked={checked} onChange={() => setChecked(checked => !checked)} /> {checked ? "checked" : "non checked"} </> ); }
第二引数の配列:依存配列
副作用が実行される条件を指定(設定した値が更新された時に実行されるようになる)
const [value, setValue] = useState(""); useEffect(() => { console.log(`typing ${value}`) });
const [value, setValue] = useState(""); useEffect(() => { console.log(`typing ${value}`) }, []);
const [value, setValue] = useState(""); useEffect(() => { console.log(`typing ${value}`) }, [value]);
(※)同一性チェックで値が同じか判定している
const a = "test"; const b = "test"; console.log(a === b); // true const arr1 = [1,2,3]; const arr2 = [1,2,3]; console.log(arr1 === arr2); // false
依存配列に、配列、オブジェクト、関数、を指定した場合は、コンポーネント再描画時に(これらのインスタンスは再生成され、非同一と判定されるため)useEffectが常時実行されてしまう。
それを防ぎ、かつ中身が変わった時のみ実行できる手段
ref: React.memo / useCallback / useMemo の使い方、使い所を理解してパフォーマンス最適化をする
例:
export const useWindowSize = () => { const [width, setWidth] = useState<number>(); const [height, setHeight] = useState<number>(); const resize = () => { setWidth(window.innerWidth); setHeight(window.innerHeight); } useLayoutEffect(() => { window.addEventListener("resize", resize); return () => window.removeEventListener("resize", resize); }, []); return [width, height]; }
export const useMousePosition = () => { const [x, setX] = useState<number>(0); const [y, setY] = useState<number>(0); type SetPositionInput = { x: number, y: number }; const setPosition = ({ x, y }: SetPositionInput) => { setX(x); setY(y); } useLayoutEffect(() => { window.addEventListener("mousemove", setPosition) return () => window.removeEventListener("mousemove", setPosition); }, []); return [x, y] }
ステート更新のロジックを抽象化可能
export const CheckBox: VFC = () => { const [checked, toggle] = useReducer(checked => !checked, false); return ( <> <input type="checkbox" checked={checked} onChange={toggle} /> {checked ? "checked" : "non checked"} </> ); }
const firstUser = { id: "", firstName: "SYM", lastName: "THY", city: "Tokyo", state: "Japan", admin: false, } type UserData = typeof firstUser; export const User: VFC = () => { const [user, setUser] = useReducer((user: UserData, newDetails: Partial<UserData>) => ({ ...user, ...newDetails }), firstUser); // Reducer を使わずにやろうとすると以下にする必要がでてくる //const onClick = () => setUser({ ...user, admin: true }) const onClick = () => setUser({ admin: true }); return ( <div> <h1>{user.firstName}{user.lastName} - {user.admin ? "Admin" : "User"}</h1> <p>Location: {user.city}, {user.state}</p> <button onClick={onClick}>Make Admin</button> </div> ); }
メモ化したコンポーネントは、プロパティが変更されない限り再描画されない
第2引数がfalseの時のみ実行される
const OnceRenderCat = memo(Cat, () => true); // 初回のみ描画 const AlwaysRenderCat = memo(Cat, () => false); // 毎回描画
export const App = () => { const [cats, setCats] = useReducer( (cats: string[], newCats: string[]) => [...cats, ...newCats], hadCats ); const onAddCat = (name: string) => { setCats(name ? [name] : []); }; return ( <> {cats.map((name, i) => { <PureCat key={i} name={name} meow={() => console.log(`${name} meowed`)} /> })} <AddCatForm addCat={onAddCat} /> </> ); }
type CatProps = { name: string, meow: (name: string) => void } const Cat: VFC<CatProps> = ({ name, meow = fn => fn }) => { console.log(`rendering cat: ${name}`); return <p onClick={() => meow(name)}>cat: {name}</p> }; export const PureCat = memo( Cat, (prevProps, nextProps) => prevProps.name === nextProps.name );
type AddCatFormProps = { addCat: (name: string) => void } export const AddCatForm: VFC<AddCatFormProps> = ({ addCat }) => { const [name, setName] = useState<string>(""); const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => { event.preventDefault(); addCat(name); setName(""); } const onChange: (event: React.ChangeEvent<HTMLInputElement>) => void = event => { setName(event.target.value); } return ( <form> <input value={name} onChange={onChange} type="text" placeholder="input name..." required /> <button>Add Cat</button> </form> ) }
Component-Based(コンポーネントベース)
Just The UI(UI にしか関知しない)
Virtual DOM(仮想DOM)
One-Way Dataflow(単方向データフロー)
子に親のstateを更新する関数を譲渡、子の変更=直接親のstate更新。(子→親の向けなので)リフトアップ
例:フォームを実装
props = properties。{ 属性名: 属性値 }
の形式。
関数コンポーネント推奨。
※ 16.3以降は componentWillMount, componentWillReceiveProps, componentWillUpdate 非推奨(いずれ完全削除予定)
Presentational Component はスタイルガイドと共存させやすく (スタイルガイドに登録することでデザインの運用に活用できてるため) 再利用性が高まる
公式が推奨するコンポーネントの正しい作り方
観点 | Presentational | Container |
---|---|---|
関心点 | どのように見えるか | どのように機能するか |
マークアップ量 | 内部にDOMマークアップを多く持つ | DOMマークアップを可能な限り持たない |
データ/振舞い | propsとして一方的に受け取る | 他のコンポーネントに受け渡す |
Flux依存度 | (Fluxの)store等に依存しない | (Fluxの)actionを実行したり、storeに依存する |
状態 | 自身の状態を持たない(UIの状態は持つ) | データの状態を持つ |
データ更新 | データの変更に介入しない | データの変更に介入し任意の処理を行う |
実装 | 関数コンポーネントで表現されることが多い | (関数コンポーネントでも可能だが)HOC、Render Props、Hooksを使うことが多い |
HOC:高階関数コンポーネント (High Order Component)
type Props = { target: string }; const HelloComponent: FC<Props> = ({ target }) => <h1>Hello {target}!</h1>; export default withTarget(HelloComponent);
Render Props:レンダリングのための関数をprops として受け取る
type Props = { target: string }; const HelloComponent: FC<Props> = ({ target }) => <h1>Hello {target}!</h1>;
<TargetProvider render={HelloComponent} /> const TargetProvider: FC<{ render: FC<Props> }> = ({ render }) => render({ target: 'Patty' });
HOC、render propsの問題
Hooks
コンポーネントの階層構造を変えることなく、状態を伴った再利用可能なロジックを追加できる
(ライフサイクルメソッドの反省のもとに)時間によって切り分けるのではなく、機能ごとに副作用を伴う処理をまとめて記述できる仕組みを提供
Hooks実装
create-react-app すると以下ができるイメージ
. |--package.json |--package-lock.json |--public | |--index.htm |--src | |--App.js | |--index.js | |--styles.css
// index.js import { StrictMode } from "react"; import ReactDOM from "react-dom"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, rootElement );
// App.js import "./styles.css"; export default function App() { return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
返却するHTML要素は、1タグで囲われていなければならない
import React from 'react'; import ReactDom from "react-dom"; const App = () => { return <h1>Hello</h1>; } ReactDom.render(<App />, document.getElementById("root"));
<div>
で囲うと余計な要素がレンダリングされる。その時は<React.Fragment>
で囲う。
<React.Fragment>
は<>~</>
で省略可
import React from 'react'; import ReactDom from "react-dom"; const App = () => { return ( <> <h1>Hello</h1> <p>topic</p> </> ); } ReactDom.render(<App />, document.getElementById("root"));
// App.jsx import React from 'react'; const App = () => { return ( <> <h1>Hello</h1> <p>topic</p> </> ); } export App;
// index.js import React from 'react'; import ReactDom from "react-dom"; import App from "./App"; ReactDom.render(<App />, document.getElementById("root"));
import React from 'react'; const App = () => { const onClickHandler = () => alert(); return ( <> <h1>Hello</h1> <button onClick={onClickHandler}></button> </> ); } export App;
CSSのプロパティ名もキャメルケースで書く
// App.jsx import React from 'react'; const App = () => { const textStyle = { color: 'blue', fontSize: '16px' }; return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <p style={textStyle}>topic</p> </> ); } export App;
// components/Message.jsx 受け取る側 import React from 'react'; const Message = (props) => { const textStyle = { color: props.color, fontSize: '16px' }; return ( <p style={textStyle}>{props.message}</p> ); } export Message;
// App.jsx 渡す側 import React from 'react'; import Message from './components/Message'; const App = () => { const onClickHandler = () => alert(); return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <Message color="blue" message="topic" /> <button onClick={onClickHandler}></button> </> ); } export default App;
// components/MessageTwo.jsx 受け取る側 import React from 'react'; const MessageTwo = (props) => { const { color, children } = props; const textStyle = { color, // プロパティ名と変数名同じなら省略可 fontSize: '16px' }; return ( <p style={textStyle}>{children}</p> ); } export default MessageTwo;
// App.jsx 渡す側 import React from 'react'; import MessageTwo from './components/MessageTwo'; const App = () => { const onClickHandler = () => alert(); return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <MessageTwo color="green">topic2</MessageTwo> <button onClick={onClickHandler}></button> </> ); } export App;
例:カウントアップ
// App.jsx 渡す側 import React, { useState } from 'react'; const App = () => { const [num, setNum] = useState(0); const onClickCountUp = () => { setNum(num + 1); }; return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <button onClick={onClickCountUp}>count up</button> </> ); } export App;
以下をベースに
// App.jsx 渡す側 import React, { useState } from 'react'; const App = () => { const [isShow, setShowFlag] = useState(true); const [num, setNum] = useState(0); const onClickCountUp = () => { setNum(num + 1); }; const onClickChange = () => { setShowFlag(!isShow); }; return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <button onClick={onClickChange}>on/off</button> {isShow && <p>★★★</p>} </> ); } export App;
機能追加する:3の倍数の時に表示(Too many re-renders.)
// App.jsx 渡す側 import React, { useState } from 'react'; const App = () => { const [isShow, setShowFlag] = useState(true); const [num, setNum] = useState(0); const onClickCountUp = () => { setNum(num + 1); }; const onClickChange = () => { setShowFlag(!isShow); }; if (num % 3 === 0) { // stateの値が変わるので無限再レンダリング setShowFlag(true); } else { setShowFlag(false); } return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <button onClick={onClickCountUp}>count up</button> <br /> <button onClick={onClickChange}>on/off</button> {isShow && <p>★★★</p>} </> ); } export App;
// App.jsx 渡す側 import React, { useState } from 'react'; const App = () => { const [isShow, setShowFlag] = useState(true); const [num, setNum] = useState(0); const onClickCountUp = () => { setNum(num + 1); }; const onClickChange = () => { setShowFlag(!isShow); }; if (num > 0) { // 対策後 if (num % 3 === 0) { isShow || setShowFlag(true); } else { isShow && setShowFlag(false); } } return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <button onClick={onClickCountUp}>count up</button> <br> <button onClick={onClickChange}>on/off</button> {isShow && <p>★★★</p>} // ボタン押下 → 再レンダリング. isShowはnumに応じて決まるため機能しない </> ); } export App;
別の機能が影響してバグる(num表示とon/off機能)。その場合はuseEffectを使って関心を分離する必要がある。
useEffect(() => { // ここに書いた処理は最初の一回しか実行されない }, []);
const [num, setNum] = useState(0); useEffect(() => { // num が変わった時だけ処理実行 }, [num]);
// App.jsx 渡す側 import React, { useState } from 'react'; const App = () => { const [isShow, setShowFlag] = useState(true); const onClickChange = () => { setShowFlag(!isShow); }; useEffect(() => { if (num > 0) { if (num % 3 === 0) { isShow || setShowFlag(true); } else { isShow && setShowFlag(false); } } }, [num]); return ( <> <h1 style={{ color: 'red' }}>Hello</h1> <button onClick={onClickCountUp}>count up</button> <br> <button onClick={onClickChange}>on/off</button> {isShow && <p>★★★</p>} </> ); } export App;
※上記だと、Eslint で以下エラーになる
/* eslint react-hooks/exhaustive-deps */
const App = () => { const [inputTodoText, setInputTodoText] = useState(""); const onChangeTodoText = (event) => setInputTodoText(event.target.value); return ( <input id="js-add-text" value={inputTodoText} onChange={onChangeTodoText} className="ul-brd ul-pad-6-16" type="text" placeholder="Todo入力" /> ); }
onClick={onClickDelete(index)} としてしまうと即時実行される
<ul> {incompleteTodos.map((todo, index) => { <button className="ul-brd" onClick={() => onClickDelete(index)}>削除</button> })} </ul>
export推奨
使う側で名前付けするので、タイポに気付きにくい
import Sample from './components/SampleComp';
使用時は分割代入必須かつ定義された名前以外はエラーになる=タイポ防止
import { SampleComp } from './components/SampleComp';
stress コマンドが使えるならば、お手軽にできるかもしれない。
使えない環境のため、以下で実施した。その記録
ご利用は計画的に、もっといい方法がありそう。
#! /bin/bash INIT_ALLOC_MEM=209715200 # 200MB * 2 消費 LOOP_NUM=2 # INIT_ALLOC_MEM * N REPEAT_ALLOC_MEM=10485760 # 10MB * 2 消費 echo PID=$$ echo "# start: init memory allocate." for i in `seq ${LOOP_NUM}` do eval a$i'=$(head --bytes ${INIT_ALLOC_MEM} /dev/zero |cat -v)' echo -n " $i" done echo echo "# end." echo -n "[ Enter : add 20 MB ] , [ Ctrl+d : stop ]" c=0 while read byte; do eval b$c'=$(head --bytes ${REPEAT_ALLOC_MEM} /dev/zero |cat -v)' c=$(($c+1)) echo -n ">" done echo
以下、メモリ量確認用shell (freeコマンドの結果から抽出)
mem_free=`free -m | awk '/Mem:/ {print $4}'` swap_free=`free -m | awk '/Swap:/ {print $4}'` buff_cache=`free -m | awk '/Mem:/ {print $6}'` total_virtual_mem=`expr ${mem_free} \+ ${swap_free} \+ ${buff_cache}` echo "Mem Free: ${mem_free} MB" echo "Swap Free: ${mem_free} MB" echo "Buff/Cache: ${mem_free} MB" echo "Total: ${total_virtual_mem} MB"