20210413のReactに関する記事は14件です。

普遍的な atom と molecule の境界を意識したコンポーネント設計

はじめに Dwango でニコニコ生放送のフロント開発を担当している @misuken です。 AtomicDesign でコンポーネントを作る際 atom と molecule をどこで分けるかに悩むことが多いと思うので、今回は普遍的な atom と molecule の境界を意識したコンポーネント設計に関しての話をします。 この記事のスコープ atom と molecule の境界のみにスコープを当てた内容になります molecule と organism の境界には触れません(最後まで読むとその理由がわかります) JSX で説明しますが、 React でも Vue でもこの記事の内容が有効です 悩ましい粒度の境界 AtomicDesign が話題になってから結構時間が経ちました。 しかし、実践してみたもののそのままだとうまくいかないところが出てくるという話を未だよく目にします。 大抵は AtomicDesign をそのまま実践することよりも、開発チーム内で共通認識を持てることを落とし所として、AtomicDesign を参考にチーム独自のルールを追加して運用していることが多いのではないでしょうか。 しかし、開発チームのメンバーごとにも捉え方の違いがあったり、チーム編成が変わったり、新しい案件でチームが変わったり、その都度折り合いを付けていくのは大変です。折り合いを付ける度に主観が衝突し、妥協が発生し、徐々にコンポーネントの秩序が失われていきます。 もっと幅広く共有できる指針があれば、開発はもっと楽になるのではないでしょうか。 ということで、普遍的な atom と molecule の境界を手に入れて、迷うポイントを減らしていきましょう。 本題 このような <button>push</button> HTML を出力するボタンのコンポーネントがあったとします。 これは誰もが atom に分類するでしょう。 しかし、アイコンと組み合わせたボタンの場合はどうでしょう。 <button><svg />push</button> IconButton として molecule にする atom はこれ以上分解できない単位じゃないといけないので、アイコンを内包した時点で molecule になる IconButton として atom にする Button を atom とする以上、他の要素を内包しても atom とし続ける Button に svg を渡せるようにするだけだから atom のままにする Button 自体を拡張するだけで IconButton というコンポーネントは作らない この時点で人によって粒度の解釈が変わり、問題が生じ始めます。 表現パターンと粒度 実際に開発を進めると、ボタンを以下のような表現パターンで使用したいシーンが頻繁に訪れます。 3つ目はアクセシビリティを高めつつ css の content: attr(aria-label); を使用してホバーツールチップボタンにする際に使用されます。 <button>push</button> <button><svg />push</button> <button aria-label="push"><svg /></button> これら表現パターンの違いで粒度を変えるべきなのでしょうか? あなたのプロジェクトのコンポーネントは探しやすいですか? コンポーネントを探しやすいというのは「あのコンポーネントどこにあったっけ?」というときに、すぐ実装されている場所がわかるということです。 例えば IconButton コンポーネントを探そうとしたとき、以下のディレクトリまで来て、あなたは atoms と molecules のどちらに進みますか? /atoms /molecules 「チームの方針による」といったところでしょうか。 では、 CloseButton を探すときはどちらに進みますか? このとき最悪のパターンは以下です。 実装で svg を含んでいなかったら atoms 実装で svg を含んでいたら molecules つまり、実装に引きずられて粒度が変わるパターンを採用したら、コンポーネントは必ず探しにくくなるということを示しています。 実装がわからないと場所を判断できない 場所がわからないと実装を確認できない これはデッドロックに陥った状態と言えます。 CloseIconButton にすればよいのでは? CloseButton と CloseIconButton で分ければ多少マシかもしれません。 しかし、本当にアイコンの有無で atoms と molecules を分けたいでしょうか? 本心はそうではないはずです。 実際の開発中にありがちなシーンをイメージしてみましょう。 閉じるボタンをここに置きたいです atoms/close-button/* 閉じるボタンにアイコンを付けられますか? atoms/close-button/* → molecules/close-icon-button/* やっぱりアイコン無いほうが良いので外してください molecules/close-icon-button/* → atoms/close-button/* 「ええと、今は閉じるボタンってどう実装されてたっけ・・・」 ちょっとした見た目の変更の度にコンポーネント名が変わったり、粒度が変わってディレクトリを行き来してしまっては、筋の良い方法とは言えません。 軽微な変更内容に対して、利用先でパスやコンポーネント名の変更が必要になるなど、破壊的変更のインパクトが大きくなりすぎてしまいます。 表現方法の違い 関心があるのは閉じるボタンであって、アイコンが付いてるかどうかは表現方法の違いでしかありません。表現方法の変更では破壊的変更が生じないようしたほうが明らかに得策です。 では、どのようにすれば変更に強いコンポーネントを作れるのかを考えてみましょう。 CloseButton を例にあげます。 以下の3つは表現は違えど、動作も意味も同じです。 <button>閉じる</button> <button><svg />閉じる</button> <button aria-label="閉じる"><svg /></button> これらの違いは全て CloseButton が良しなに吸収してくれたほうが使いやすいコンポーネントであると言えます。 さらに突き詰めると、 "Close" の部分に依存しているのはアイコンとテキストの内容だけなので、値とレイアウトに分離し Button の標準機能で吸収できることがわかります。 // 実装イメージ const closeButtonProps = { symbolIcon: <svg />, text: "閉じる" }; // 同じ props を渡し、レイアウトの指定だけで表現方法を切り替える <Button layout="textOnly" {...closeButtonProps} /> <Button layout="default" {...closeButtonProps} /> <Button layout="ariaLabel" {...closeButtonProps} /> こうすることで、 Button コンポーネントが表現の違いを吸収する機能を提供するようになり、表現の違いによる破壊的変更の発生を防げます。 その他の表現パターン Button と同様の表現パターンは Anchor や Item(メニューの項目) といったテキストを表現する系のコンポーネントで広く共通する部分です。 シンボルとなるアイコンだけでなく、以下のような表現が必要になる場合もあります。 ショートカットキーの表示 リスト項目の右端に表示される > などの項目選択時のアクションを表すアイコン これらも基礎的なコンポーネントで吸収することで、粒度を安定させることができます。 粒度の境界 ここまでの説明を踏まえ、改めて粒度の境界を探ってみましょう。 表現パターンの違いで内包する要素が現れたとしても、それは粒度に影響を与えないということがわかっているので atom と molecule の境界がどこなのかを表現するとこうなります。 内包する構成要素が無ければ意味を成さないコンポーネントかどうか です。 molecule の例 構成要素を持つことが前提で、内包する構成要素が無ければ意味を成さないコンポーネントは molecule になります。 例えば、 List や Form といったコンポーネントは molecule になります。 出力する HTML が <ul></ul> や <form></form> だけの状態なら atom と言えなくもないかもしれませんが、実際に利用する際は必ず内包する構成要素が存在するので List や Form は molecule です。 Button は常に atom Button の場合はアイコン等の要素を内包する場合がありましたが、それは表現の違いです。内包する構成要素が無くてもボタンとしての意味を成しており、その点で molecule とは明確な違いが存在します。 これはボタンが主体のコンポーネントなら、中に何が入ろうがそれは atom の条件を満たしていることを表し、それらのコンポーネント名の末尾は常に Button で終わるということを意味します。 画像のボタンの例 例えばテキストの代わりに画像 <img src="foo.png" alt="push" /> が表示されたとしましょう。 画像に文字が書いてあるかもしれませんし、画像にイラストが描いてあるかもしれませんが、画像の内容によって粒度が変わるのは不自然です。テキストの文字列で表示しようが、画像の文字列で表示しようが、伝えようとしていることやボタンとしての機能に変化はありません。 やはりこれらは表現の違いでしか無く、本質的にはただのボタンであると言えます。 普遍的な粒度の境界 これらを踏まえると、普遍的な Button = atom の関係が定まっていると言えるでしょう。 他の全てのコンポーネントに対しても同じ方法で整理できるので、コンポーネント名の末尾を見れば、それが atom か molecule かを判断できるようになります。 このようにコンポーネントの末尾に UI の型を表す名前を明示する手法は BCD Design で有効な手段であることがわかっているので、是非参考にすると良いでしょう。 改めて問います 以下のディレクトリ構造で CloseButton はどっちにありますか? これまでの内容を理解していれば明確に atoms にあることを説明できます。 /atoms /molecules これがわかりやすさであり、個人の主観や特定のチームに依存しない普遍的な atom と molecule の境界であると言えるでしょう。 粒度は重要なのか? ここまで、 AtomicDesign の粒度の話をしてきましたが、実はコンポーネントの粒度はそこまで重要ではないとも感じています。 概念軸で分類する BCD Design を使用すると、粒度軸で得られるメリットは依存関係の明確化くらいしかなく、概念軸で得られる関心の凝集のメリットのほうが圧倒的に大きくなるためです。 もちろん、より精度を高めていく上では粒度軸も正確であることに越したことはないのですが、多くの場面では概念軸で整理すると、さらに粒度軸で整理ほどの複雑度が残らないので概念軸だけで済んでしまいます。 AtomicDesign は molecule と organism の分類が曖昧になりやすい問題もあり、 atom と molecule を見極められるようになった先にも困難が待ち受けます。 BCD Design は organism 不要で AtomicDesign で抱えるモヤモがスッキリ解消できるため、より本質的な解決を目指す場合は "概念軸" を学んでみてはいかがでしょうか。 もっと奥の深い粒度の話 TextBox SelectBox ComboBox などでもっと奥深い粒度の話を掘り下げられるのですが、長くなったので今回はここまでにしておきます。 2021-04-14 意外と単純ではない TextBox が atom である理由 を公開しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReduxToolkitとcreateContext,useReducerを使ってProviderを作成しグローバルにstateの値を管理する

はじめに reactを使う中でStateの管理にProviderを作る事が多いため、忘れないようにメモ。 ReactとTypescriptで書いています。初心者のため、間違っているところもあるかもしれません。 目次 インストール createSliceの作成 ContextとProviderの作成 使い方 インストール ターミナル $ yarn add @reduxjs/toolkit react-redux $ yarn add -D @types/react-redux createSliceの作成 provider.tsx import React,{createContext,useReducer} from 'react'; import { createSlice, PayloadAction } from '@reduxjs/toolkit'; //Stateのタイプを設定 export type KakeiboState = { detailPlan:Array<number>; sumDetailPlan:number; resultPlan:Array<number>; sumResultPlan:number; } //Stateの初期値を設定 const initialState:KakeiboState = { detailPlan :[...Array(5)].fill(0), sumDetailPlan :0, resultPlan:[...Array(5)].fill(0), sumResultPlan:0, } // createSliceでreducerとactionを同時に定義 const kakeiboSlice = createSlice({ name: 'kakeibo', initialState, reducers: { setDetailPlan: (state, action: PayloadAction<Array<number>>) => { return{ ...state, detailPlan: action.payload, } }, setSumDetailPlan: (state, action: PayloadAction<number>) => { return{ ...state, sumDetailPlan: action.payload, } }, setResultPlan:(state,action:PayloadAction<Array<number>>) => { return{ ...state, resultPlan:action.payload } }, setSumResultPlan:(state,action:PayloadAction<number>) => { return{ ...state, sumResultPlan:action.payload } }, setInitialState:() => ({ ...initialState, }) }, }) //actionとしてエクスポートする export const { setDetailPlan, setSumDetailPlan, setResultPlan, setSumResultPlan, setInitialState } = kakeiboSlice.actions; ContextとProviderの作成 provider.tsx //Providerに使う型の設定 type KakeiboProviderValue = { kakeiboInfo:KakeiboState; kakeiboDispatch:React.Dispatch<PayloadAction< |number |Array<number> > >; }; //コンテクストの作成 export const KakeiboContext = createContext({} as KakeiboProviderValue); type KakeiboProviderProps = { children:React.ReactNode; }; //useReducerを使ってProviderの作成 const KakeiboProvider:React.FC<KakeiboProviderProps> = (props:KakeiboProviderProps) => { const [kakeiboInfo,kakeiboDispatch] = useReducer( kakeiboSlice.reducer,initialState); return( <KakeiboContext.Provider value={{kakeiboInfo,kakeiboDispatch}}> {props.children} </KakeiboContext.Provider> ); }; export default KakeiboProvider 使い方 ・Providerを使用したいファイルをProviderで挟みます。間にいくつ挟んでも大丈夫です。 App.tsx import React from 'react'; import './App.css'; import Kakeibo from './kakeibo/Component/main'; import KakeiboProvider from '../src/kakeibo/Container/Provider/provider'; const App:React.FC = () => { return ( <KakeiboProvider> <Kakeibo /> </KakeiboProvider> ); } export default App;  インポート文 Kakeibo.tsx //useContextとProviderで作成したKakeiboContextをインポートします。 //stateの値を変更したい処置がある場合は、reducerに書いたものからインポートします。この場合はsetSumDetailPlanです。 import React,{useContext} from 'react' import {KakeiboContext,setSumDetailPlan} from '../Provider/provider';  使用する時 Kakeibo.tsx //Providerで宣言したものを使います。 const {kakeiboInfo,kakeiboDispatch} = useContext(KakeiboContext); //Providerから値を取得する時 conosle.log(kakeiboInfo.sumDetailPlan) //現在のstateに入っているsumDetailPlanの値が返される //Providerを使ってstateを変化させる時 kakeiboDispatch(setSumDetailPlan(5)) //state中のsumDetailPlanに「5」が設定される
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【とりあえずハンズオン】EC2でReactを動かすだけの話

はじめに この記事では EC2 上で React を動かすだけのハンズオン記事です。 ベストプラクティスや間違いがあれば 書き直していく予定です。 環境 ヤマダにペペロンチーノをこぼされた初代 Surface Book 実装 RAM 8GB エディション Windows 10 Pro バージョン 20H2 OS ビルド 19042.867 Chrome 89.0.4389.90(Official Build) (64 ビット) ハンズオンセットアップ ざっくり、流れを説明 AWS 上に EC2 を構築 Visual Studio Code で Remote SSH をインストール Remote SSH の設定 EC2 に React をインストール React を ローカルホストで実行 AWS 上に EC2 を構築 まずはコンソールを開いて。。。 と以前から CloudFormation の記事で EC2 の構築をやっているので 今回は IaC で構築します。 VPC を構築 まずは EC2 を置くためのサブネットを切る為に VPC を CloudFormation で構築します。 CloudFormation の管理画面を開きます。 スタックの作成をクリック vpc_network.yml AWSTemplateFormatVersion: 2010-09-09 Parameters: VpcCidrBlock: Description: VpcCidrBlock Type: String Default: 10.0.0.0/21 Resources: MainVpc: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidrBlock EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: MainVpcfromCF IGW: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: igw Value: igw-cf AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref MainVpc InternetGatewayId: !Ref IGW Outputs: MainVpc: Value: !Ref MainVpc Export: Name: MainVpcId MainIgw: Value: !Ref IGW Export: Name: MainIGWId ファイル選択から vpc_network.yml をアップロードします。 スタック名は「vpc_network」とします。 サブネットを切る VPC が構築できたらサブネットを切ります。 今回は React アプリケーション公開用にセキュリティグループに TCP でポート 80 を開けます。 ReactSubnet.yml AWSTemplateFormatVersion: 2010-09-09 Parameters: AZ: Description: AvailabilityZone Type: String Default: "ap-northeast-1a" PublicSubnetCidrBlock: Description: PublicSubnetCidrBlock Type: String Default: 10.0.2.0/24 Resources: PublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Ref AZ VpcId: !ImportValue MainVpcId CidrBlock: !Ref PublicSubnetCidrBlock Tags: - Key: Name Value: PublicSubnet PubRT: Type: AWS::EC2::RouteTable Properties: VpcId: !ImportValue MainVpcId Tags: - Key: PublicRoute Value: PublicRouteCf PubToInternet: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PubRT DestinationCidrBlock: 0.0.0.0/0 GatewayId: !ImportValue MainIGWId PubRTAssociate: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet RouteTableId: !Ref PubRT secSSHGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: PublicSecGrp GroupDescription: PublicSecGrp VpcId: !ImportValue MainVpcId SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: PublicSecGrp Outputs: PublicSubnet: Value: !Ref PublicSubnet Export: Name: PublicSubnet PublicSecGrp: Value: !GetAtt secSSHGroup.GroupId Export: Name: PublicSecGrp ファイル選択から ReactSubnet.yml をアップロードしてスタックを作成 スタック名を「react-subnet」とします。 スタックの作成には時間がかかりますので少し待ちましょう。 キーペアを作成する AWS コンソールから EC2 を開き、左ペインからキーペアを選択 キーペアを作成をクリック キーペアを「Mykeypair」という名前で作成 EC2 を構築する CloudFormation の画面に戻り、EC2 を作成する。 public_react_ec2.yml AWSTemplateFormatVersion: 2010-09-09 Resources: myEC2Instance: Type: AWS::EC2::Instance Properties: KeyName: Mykeypair ImageId: ami-0992fc94ca0f1415a InstanceType: t2.micro Monitoring: false NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: "0" GroupSet: - !ImportValue PublicSecGrp PrivateIpAddress: "10.0.2.10" SubnetId: !ImportValue PublicSubnet Tags: - Key: Name Value: PublicEC2 ファイル選択から public_react_ec2.yml をアップロード スタックの完成予想図 セキュリティグループの確認 SSH 用のポートと公開用のポート番号が開かれていることを確認しましょう。 Visual Studio Code の設定 Visual Studio Code で Remote SSH をインストール EC2 に SSH でログインする為に VisualStudioCode のプラグインである「Remote SSH」をインストールします。 まだ、インストールをしていない人は以下のようにインストールと表示されていると思います。 インストールされている方は以下 Remote SSH の設定 Visual Studio Code の拡張機能 Remote SSH を設定します。 まず、Visual Studio Code の画面左下の緑ボタンを押します。 するとウィンドウの上部に選択肢が出るので「Connect to Host...」を選択 続いて「Configure SSH Hosts」を選択 Remote SSH の格納先を問われるのでユーザ名配下の.ssh を選択 以下の内容を環境に合わせて記載。 Host React-EC2 HostName {パブリックIPアドレス} IdentityFile {キーペア名(フルパス)} User ec2-user コンフィグを保存して閉じる。 再度、緑のボタンを押して Remote SSH 接続を開始すると新しいウィンドウが開かれる。 point The authenticity of host '54.178.82.82 (54.178.82.82)' can't be established. ECDSA key fingerprint is SHA256:sDZUv1irzZeafPa0UzdyNMmI0SGAViB5/ZTmRJ4vwG0. Are you sure you want to continue connecting (yes/no)? SSH 接続する前にこんなことを聞かれたら yes と打って次へ React を試す EC2 React のセットアップ 「Open Folder 」を選択 ディレクトリを選択 ec2-user のホームディレクトリ「/home/ec2-user/」を選択 Visual Studio Code のウィンドウ左ペインから ファイルを新規作成 以下のような Bash スクリプトを書いて EC2 の「/home/ec2-user/」に保存する。 react-nginx.sh # !/bin/bash sudo yum update -y && \ sudo amazon-linux-extras install -y nginx1.12 && \ sudo systemctl start nginx.service && \ sudo systemctl enable nginx.service && \ sudo yum -y install git && \ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \ . ~/.nvm/nvm.sh nvm install node && \ git clone https://github.com/ymd65536/react.git && \ cd react && \ sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf_backup && \ sudo cp ./nginx.conf /etc/nginx/nginx.conf && \ sudo systemctl restart nginx && \ bash ./react_install.sh react_install.sh は react リポジトリに格納 react_install.sh # !/bin/bash cd quick-react && \ npm install && \ npm run build && \ npm run start echo "React Setup Complete!!" &(アンパサンド)を使うことでコマンドの実行がこけた時の対策ができる。 例えば、今回のケースだと最初の yum がこけた場合は以降のコマンドは実行されない。 また、バックスラッシュでコマンド同士をつなぐとコマンド同士をつないでから実行するという意味になる。 作成した Bash スクリプトを実行する。 HTTP で開く http://localhost:3000/ を開く ※npm run start すると自動で開かれる。 ちなみに まとめ 今回は CloudFormation で VPC と EC2 を構築して React 環境を建てました。 次回はこの React をコンテナに入れたいと思います。 CloudFormation にスクリプトを埋め込めばもっと楽にできたなこれ。(あとの祭り) おわり
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 - Part1 - VsCodeをインストールしてReactでHello World

目次 React入門 - Part1 - VsCodeをインストールしてReactでHello World★★ここです★★ React入門 - Part2 - Next.jsを使ってページ遷移を行う React入門 - Part3 - コンポーネントを書いてみる React入門 - Part4 - 親子間コンポーネントへの値の受け渡し node.jsのインストール ここから環境に応じたものをダウンロードしインストールします。 ここにあるように「Automattically install the necessary tools. …」にはチェックを入れずインストールしました。 VsCodeのインストール ここにあるようにダウンロードしてインストールしてください。 日本語化の手順等は、ググるとすぐ出てくるので、ググって対処ください。 GitBashのインストール 何かと使用するのでダウンロードしインストールしておきます。 ここを参考にしてください create-react-appを、コマンドラインツールからグローバルにインストール ここにあるようにをグローバルインストールする $ npm install -g create-react-app Hello World用プロジェクト作成、Hello World表示 プロジェクトを作成したいディレクトリまで移動し、以下コマンドを実行する。 ※実行エラーになった場合、ここを参考にスクリプトの実行権限を変更する。 $ create-react-app hello-world-prj http://localhots:3000 で以下のような画面が表示されれば成功です。 ※Hello Worldでは無かったです。。。。Learn Reactでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactNativeで「続きを読む/続きを見る」を実装する

省略したTextコンポーネントを展開するUIを実装する方法 React Nativeで、インスタやTwitterなどの省略した文章を展開する「続きを見る」ボタンを実装する方法がなかなか見つからなかったので作ってみた。 展開した状態で行数を取得し、その後すぐに縮小している。省略・展開にはnumberOfLinesを利用している。 以下は行数が2行より多い場合に、続きを見るボタンを表示するサンプルである。 投稿のキャプション部分を実装した例 const Caption = ({navigation, text}) => { const onTextLayout = ({nativeEvent: {lines}}) => { // 展開された状態の行数を取得する if (typeof lineCount === "undefined"){ setLineCount(lines.length) setIsExpanded(false) } } const [lineCount, setLineCount] = useState() // はじめはundefinedにしておく const [isExpanded, setIsExpanded] = useState(true) return ( <Text numberOfLines={isExpanded ? undefined : 2} onTextLayout={onTextLayout}>{text}</Text> // 行数が2行より多く、省略されている場合のみ「続きを見る」ボタンを追加 {lineCount > 2 && !isExpanded && <Text onPress={() => setIsExpanded(true)}>続きを見る</Text>} ) } より良い方法があれば、ぜひ教えて下さい!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SkyWayチュートリアル 〜React,TypeScript仕立て〜

はじめに アプリやWebサービスにビデオ会議や音声通話などを導入するためのSDKであるSkyWayのチュートリアルをReactとTypeScriptの環境でやってみました。冗長な部分やナンセンスな部分など多々あるかと思いますが、温かく見守ってください。そういう部分や、間違いががあればどしどしダメ出ししてください! 下記サイト(JavaScriptSDK)に沿って進めていくので、別タブで開いて、見比べると見やすいかなと思います。 ひとまず、create-react-appでプロジェクト作成しておきます。 SDKのダウンロード yarnでやりました。 yarn add skyway-js チュートリアル 1,準備 ここはチュートリアルに従ってすすめるだけ。 ローカルサーバーの準備 「1-2のローカルサーバーの準備」ではLiveServerとか色々書いてますが、今回はyarn startをしました。 2,アプリケーションの作成 JSX作成 公式ではhtmlを編集していますが、今回はReactなのでApp.tsxにJSXで書いていきます。 import React from "react"; function App() { return ( <div className="App"> <div>React TypeScript SkyWay</div> </div> ); } export default App; CDNのインポート 公式サイトでは2-2でCDNでSDKをインポートしていますが、今回は使わないのでスルーします。 カメラ映像、マイク音声の取得 まずはカメラ映像を表示する領域としてvideo要素を追加します。 import React from "react"; function App() { return ( <div className="App"> <div>React TypeScript SkyWay</div> <video ref={ref} width="400px" autoPlay muted playsInline></video> </div> ); } export default App; 公式サイトで示されているvideoタグをコピーして貼り付けるだけなのですが、そのままだとautoplay属性とplaysinline属性に赤線でエラーが表示されます。それぞれautoPlay、playsInlineのようにキャメルケースで記載するとエラーが解消できます。 次に、Webブラウザでカメラ映像、マイク音声を取得するための処理なのですが、公式サイトでは素のJavaScriptで書かれているため、getElementByIdでvideo要素を取得していますが、Reactでこれをやるのはご法度?なので、Refを使用してvideo要素にアクセスできるようにします。 import React, { useState, useRef } from "react"; let localStream: MediaStream; function App() { const ref = useRef<HTMLVideoElement>(null); let [localStream, setLocalStream] = useState<MediaStream>(); const getMedia = () => { navigator.mediaDevices .getUserMedia({ video: true, audio: true }) .then((stream) => { const videoElm = ref.current; if (videoElm) { videoElm.srcObject = stream; videoElm.play(); } localStream = stream; }) .catch((error) => { console.error("mediaDevice.getUserMedia() error:", error); return; }); }; getMedia(); return ( <div className="App"> <div>React TypeScript SkyWay</div> <video ref={ref} width="400px" autoPlay muted playsInline></video> </div> ); } tsconfig.jsonで"noImplicitAny": trueに設定している場合、localStreamを発信処理の引数として使用するときにanyではないかと指摘されるので、MediaStream型を指定しています。 ここまで実装し、localhostにアクセスするとPCのカメラで撮影された画像が表示されていると思います。 Peerの作成 上部のreactのimport文の下でPeerをskyway-jsからimport。 import React, { useState, useRef, useEffect } from "react"; import Peer from "skyway-js"; const peer = new Peer({ key: "※※※※※※※※※※※※※※※※※※※※※※※※※※※", //チュートリアルの1,準備で取得したkey debug: 3, }); PeerID取得 skywayにおいて電話番号にあたるPeerIDを取得し、useStateを使いstateとして保存する。 import React, { useState, useRef, useEffect } from "react"; import Peer from "skyway-js"; const peer = new Peer({ key: "※※※※※※※※※※※※※※※※※※※※※※※※※※※", debug: 3, }); let localStream; function App() { const ref = useRef<HTMLVideoElement>(null); const [peerId, setPeerId] = useState<string>(""); useEffect(() => { const getMedia = () => { navigator.mediaDevices .getUserMedia({ video: true, audio: true }) .then((stream) => { const videoElm = ref.current; if (videoElm) { videoElm.srcObject = stream; videoElm.play(); } localStream = stream; }) .catch((error) => { console.error("mediaDevice.getUserMedia() error:", error); return; }); }; getMedia(); peer.on("open", () => { setPeerId(peer.id); }); }, []); return ( <div className="App"> <div>React TypeScript SkyWay</div> <video ref={ref} width="400px" autoPlay muted playsInline></video> <p>{peerId}</p> </div> ); } export default App; useEffectを使用していないと、peeridをstateとして保存しているため、getMedia関数の実行中に、stateが更新されると、Appコンポーネントが再レンダリングされ、The play() request was interrupted by a new load request.というエラーが発生しました。そのため、useEffectの中に処理を書き、第2引数として空配列を与え、初回レンダリング時にしか実行されないようにしています。 この時点でyarn startすると映像の下に、取得したidが表示されているはず。 発信処理 すでに作成しているJSXに下記を追加。 <textarea value={theirId} onChange={handleChange}></textarea> <button onClick={handleCall}>発信</button> <video ref={theirRef} width="400px" autoPlay muted playsInline></video> video要素は相手の映像を配置する場所で、後からDOM操作するためref属性に新たなrefを指定しておく。 textarea要素は発信相手のpeerIdを入力するための要素で、入力された値はuseStateを使いstateとして管理する。 button要素をクリックすると、発信処理を行うcallメソッドが呼ばれるようにhandleCall関数を作成。callメソッドは、第1引数にpeerId(ここではtheirIdという変数)、第2引数に最初に用意したカメラ映像(localstream)を指定する。 const handleCall = () => { const mediaConnection = peer.call(theirId, localStream); setEventListener(mediaConnection); }; callメソッドは、相手に接続したときに、相手の映像が含まれるMediaConnectionオブジェクトを取得する。上記のコードではそれをmediaConnection変数に代入し、その変数を引数としてsetEventListener関数に渡している。 setEventListener関数では、相手の映像を取得したときに発生する、mediaConnectionのonメソッドでstreamイベントを指定する。 const setEventListener = (mediaConnection: MediaConnection) => { mediaConnection.on("stream", (stream: MediaStream) => { const videoElm = theirRef.current; if (videoElm) { videoElm.srcObject = stream; videoElm.play(); } }); }; コールバック関数内ではsetEventListener関数の引数mediaConnectionの型は,: MediaConnectionを指定。何もしないとエラーが発生するので、下記のようにコード上部でimportしておく。 import Peer, { MediaConnection } from "skyway-js"; 着信処理 Peerオブジェクトのonメソッドで、callイベントを指定し、相手からの着信に備える。ここに関しては、チュートリアルのコードのままで良い。 最終形 import React, { useState, useRef, useEffect } from "react"; import Peer, { MediaConnection } from "skyway-js"; const peer = new Peer({ key: "※※※※※※※※※※※※※※※※※※※※※※※※※※※", debug: 3, }); let localStream: MediaStream; function App() { const myRef = useRef<HTMLVideoElement>(null); const theirRef = useRef<HTMLVideoElement>(null); const [peerId, setPeerId] = useState<string>(""); const [theirId, setTheirId] = useState<string>(""); useEffect(() => { const getMedia = () => { navigator.mediaDevices .getUserMedia({ video: true, audio: true }) .then((stream) => { const videoElm = myRef.current; if (videoElm) { videoElm.srcObject = stream; videoElm.play(); } localStream = stream; }) .catch((error) => { console.error("mediaDevice.getUserMedia() error:", error); return; }); }; getMedia(); peer.on("open", () => { console.log("open"); setPeerId(peer.id); }); }, []); const handleCall = () => { const mediaConnection = peer.call(theirId, localStream); setEventListener(mediaConnection); }; const setEventListener = (mediaConnection: MediaConnection) => { mediaConnection.on("stream", (stream: MediaStream) => { const videoElm = theirRef.current; if (videoElm) { videoElm.srcObject = stream; videoElm.play(); } }); }; peer.on("call", (mediaConnection) => { mediaConnection.answer(localStream); setEventListener(mediaConnection); }); const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { setTheirId(e.target.value); }; return ( <div className="App"> <div>React TypeScript SkyWay</div> <video ref={myRef} width="400px" autoPlay muted playsInline></video> <p>{peerId}</p> <textarea value={theirId} onChange={handleChange}></textarea> <button onClick={handleCall}>発信</button> <video ref={theirRef} width="400px" autoPlay muted playsInline></video> </div> ); } export default App; 終わりに 長くなりましたが、最期まで読んでいただけのであればありがとうございました!! 先輩方にアドバイス、ダメ出しなど頂けると非常にうれしく思います! お願いいたします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactJSプロジェクトindex.jsとindex.htmlの役割

reactプロジェクト基本構造 npx create-react-appコマンドを使ってreactプロジェクトを生成すると以下のようなフォルダ構成が生成されます。(本記事では説明に必要であるファイルだけ記載しました) src | |ーApp.js |ーindex.css |ーindex.js public | |ーindex.html 上記のファイル(index.js,index.html)はどんな役割かな。。気になって少し気合を入れて調べてみました。 Index.htmlファイル Wikiにspaを検索すると以下のように説明しています。 「単一のWebページのみから構成することで、デスクトップアプリケーションのようなユーザ体験を提供するWebアプリケーションまたはWebサイトである」 単一のwebページのみからの表現が凄くきになりますよね。 気づいた方が多いと思いますがここの単一のwebページはpublicフォルダの下にあるindex.htmlになります。 index.jsファイル Index.jsファイルはreactアプリが実行される際、最初に実行されるjavascriptファイルになります。 少しindex.jsフィアルの中にをみてみましょう! index.js import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); index.jsとindex.htmlの関係 ReactDOM.render(<App />, document.getElementById('root’));このコードをしっかり確認する必要があります。 上記のコードを人間が理解できるように翻訳するとドキュメントの中にrootと言うIDを持っているヤツの中にを描画して!くらいの表現になります。 なんとなく分かったけど。。。documentオブジェクトは何処から来る?と思ってませんか。 この謎はindex.htmlフィアルを見ると無くなります。 index.htmlのbodyだけ持ってきました。 index.html <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> divタグを見るとなんか先見たコードと関係があるらしいです。 そうですねえ!!index.jsのdocument.getElementById(‘root’)はindex.htmlの<div id=“root”>を指名しています。 上記のコードを人間が理解できるように翻訳するとドキュメントの中にrootと言うIDを持っているヤツの中にを描画して!表現をもっと詳しくすると コンポナントをindex.htmlの中にelementのidがrootになっているやつの中に描画して!になります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(備忘用)React Refの関数がいつからか変わってた

ReactのRefの関数が一部変わってました。 他にもあるかもしれませんが、備忘用に。 scrollResponderScrollToはレンダリングされたときのスクロール位置を指定できる関数です。 関数自体が変わったのかもしれませんが、動きはいまのところ同じっぽいです。 ※変更になったversionは正確にあってないと思うのでご参考までに。。。 旧(~v16.8.6?) scrollViewRef: React$ElementRef<*> = React.createRef(); render() { < onWillFocus={() => { this.scrollViewRef.current.scrollResponderScrollTo({ // この関数 y: 0, animated: false, }); }} /> } 新(v17.0.1~?) scrollViewRef: React$ElementRef<*> = React.createRef(); render() { < onWillFocus={() => { this.scrollViewRef.current.scrollResponderZoomTo({ // この関数 y: 0, animated: false, }); }} /> }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

reactjs-popupはオートフォーカスされる

reactjs-popup を使ってポップアップを作成した ポップアップの中のエレメントにモーダル、ツールチップの中にinputタグ、buttonタグを配置していた モーダル、ツールチップを開いたときに、オートフォーカスされた オートフォーカスされたくない! なぜオートフォーカスされたのか そういう仕様だったから。 それをコントロールするpropsもない! https://github.com/yjose/reactjs-popup/blob/655ea28b90b7bb1c1c7902b162e56f6823550361/src/index.tsx#L141 解決方法 これをreactjs-popupにわたすchildrenの先頭に入れる <input type="hidden"></input>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 - Part3 - コンポーネントを書いてみる

目次 React入門 - Part1 - VsCodeをインストールしてReactでHello World React入門 - Part2 - Next.jsを使ってページ遷移を行う React入門 - Part3 - コンポーネントを書いてみる★★ここです★★ React入門 - Part4 - 親子間コンポーネントへの値の受け渡し コンポーネントの記述方法 実は、React入門 - Part2 - Next.jsを使ってページ遷移を行うで既にコンポーネントを記述しちゃっいました。 React.FC ってやつです。これはReact+typescriptでの記述方法で、 Reac.FunctionalComponentの省略系のようです。 コンポーネントの書き方には、 クラスコンポーネント typescriptを用いないファンクショナルコンポーネント などもありますが、ここではtypescriptのみの紹介とします。 ※クラスコンポーネントは、react hooksが出てくるまではよく使用されていたようですが、 たぶん今ではあまり使われない記述方法かと思います。 親子コンポーネントを書いてみる 私が経験したプロジェクトでは、eslintやprettierなどが設定されていて、且つ1コンポーネント300行までなど色々コーディング規約が自動で設定されていました。 また、React自体がコンポーネント思考ということもあり、役割でコンポーネントを分ける場面というのはよくあります。 親子関係のコンポーネントを作ってみましょう。 React入門 - Part2 - Next.jsを使ってページ遷移を行う で行ったようにプロジェクトを作成し、next.jsへようこそが表示できるところまで準備しておきます。 4つのファイルをpages配下に作成します。 (私には3人の男の子供がいるので、人数分の子コンポーネントを作りました。) parent.tsx ⇒ 親コンポーネント child1.tsx ⇒ 子(長男)コンポーネント child2.tsx ⇒ 子(次男)コンポーネント child3.tsx ⇒ 子(三男)コンポーネント parent.tsx import React from 'react' import Child1 from './child1'; import Child2 from './child2'; import Child3 from './child3'; export const Parent: React.FC = () => { /** レンダー部分 */ return ( <> <span>{'私は親です'}</span> <ul> <Child1 /> <Child2 /> <Child3 /> </ul> </> ) } export default Parent; child1.tsx import React from 'react' export const Child1: React.FC = () => { /** レンダー部分 */ return ( <li>{'私は長男です'}</li> ) } export default Child1; child2.tsx, child3.tsxは ファイル名が違うのと、 コンポーネント名Child1がChild2,Child3になるのと <li>{'私は長男です'}</li> の部分が次男、三男になるだけです。 各ファイル作成、実装が完了したら http://localhot:3000/parent にアクセスして見てください。 以下のように表示されたら完成です。 ソース眺めてたらなんとなく雰囲気が分かると思いますので中身については解説しません。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPA公開時の、S3+CloudFrontとS3の違い

introduction Reactで作ったシングルページアプリケーション(SPA)を公開する際には、CloudFront+S3が多い。この構成は、Reactの挙動にどれくらいcriticalなのか気になったので、実際にSPAをホスティングしてみた。その調査・作業Log。 CloudFront+S3の場合に気になった結果はなかったので、S3のみでのホスティングを中心にまとめる。 AWSサービス S3: ストレージサービス。htmlをアップロードしてホスティングをすることができる。 CloudFront: コンテンツ配信サービス。ここを経由してS3にアクセスできる。 準備 react-router-domのQuickStartでページ遷移のあるReactのSPAを作成。 1のSPAをbuildしたものを、公開するS3バケットにアップロード。 S3をマネジメントコンソールで設定。 CloudFrontをマネジメントコンソールで設定。 AWSの設定は、参考文献を初めてとして様々な記事があるので省略。 S3でのルーティング設定 SPAのホスティング問題として、ルーティングでpathが変更された後にリロードすると、そのままだと404になってしまうという問題があるらしい。 CloudFrontでは、エラー時のリダイレクトで解決していたが、S3でも同じ設定を行うことができる。 これで解決できるかどうかを検証。 結果 S3のみでも、SPAの挙動に問題はなかったと思う。 気になった点は、エラーのリダイレクトの際に404が返ってしまうので、statusCodeを用いて制御している場合は、意図しない挙動になるかもしれない。 できたこと SPAのルーティングは確認できた。 リロード時に、リロードした時のpathに移動できる。 できなかったこと httpsにはできない。(S3の公式Document、SSLはサポートしていない。) エラーのリダイレクトの際に、404のままになってしまう。CloudFrontの場合は、200に変えることができる。 S3のリダイレクトルールを利用する場合、リロード時にindex.htmlに移動してしまう。 利用したS3のリダイレクトルールは、次のもの。 [ { "Condition": { "HttpErrorCodeReturnedEquals": "404" }, "Redirect": { "HostName": "HOST_NAME", "Protocol": "http", "ReplaceKeyWith": "index.html" } } ] 課題 未確認 APIを呼び出した際の挙動は未確認。 stateの保存等については未確認。 上記の内容は、S3とCloudFrontでできることに差がないと思うので、多分問題はない。 知識不足 S3へのアクセス権。今回行ったバケットポリシー(かなり緩い)で、セキュリティ上問題がないのかということ。 httpではなぜダメなのか。http/httpsの基礎知識。 S3へのアクセス方法。REST API エンドポイントとウェブサイトエンドポイントの主な違い。 感想 自分で作ったSPAを公開するのであれば、S3でもできそう。 一方で、Amprifyを使った方が簡単な感じもしているので、どうしようかなぁという気分。 参考文献 S3のみの場合。考えていた作業方針でできそうということは下の記事でわかりました。 またSPAのホスティングで、リロード以外に問題になる点が他になさそうなことを把握できました。 CloudFront利用の場合。定番構成のためいろいろありますが、CloudFrontの設定は下の記事を参考にしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 Part.2 Next.jsを使ってページ遷移を行う

Next.jsの概要 Next.js(ねくすとじぇーえす)とは、Reactの機能を拡張するためのJavaScriptフレームワークです。Next.jsを使用することで、サーバ側でページを生成するサーバサイドレンダリング(SSR)ができるようになります。サーバサイドレンダリングを行い、クライアント側のページ生成処理を減らすことで、より高速なウェブページの表示が可能になります。 ※ここからの抜粋 Next.jsはサーバーサイドレンダリング(SSR)やファイルベースルーティングなど多くの機能をゼロコンフィグで提供してくれます。 ※ここからの抜粋 Next.jsを設定する ここ の「セットアップ」から「もう一度yarn devを実行すると・・・」 でOKです。 纏めます。 Git Bash等でプロジェクトを作成したいディレクトリに移動して以下を実行します。 npx create-next-app プロジェクト名 cd プロジェクト名 tsconfigを配置するために、以下のコマンドを実行します。 touch tsconfig.json yarn devを実行しますが以下のようにエラーになります。 何かが足りないので「yarn add・・・」を実行しなさいということなので、それに従い以下を実行します。 yarn add --dev typescript @types/react 再度、以下を実行します。 yarn dev ブラウザでhttp://localhost:3000にアクセスすると以下の画面になります。 Next.jsでのページ遷移の概要 Next.jsはpages/ディレクトリに置いたフォルダ/ファイルの構成に従って、HTMLを生成してページ遷移を実現します。ルーティングライブラリは不要で、URLの構造に合わせてjs(ts)ファイルを配置するだけです。 以下の例ではindex.tsxが/、hoge.tsxが/hogeに対応付けられます。 --pages |--index.tsx -> / |--hoge.tsx -> /hoge |--fuga.tsx -> /fuga |--404.tsx |--_app.tsx -> アプリケーションエントリーポイント |--_document.tsx -> HTMLドキュメント構造記述用 Next.jsでページ遷移を試してみる pages配下にpage1.tsxを作成します。 import Router from 'next/router'; import React from 'react'; export const Page1: React.FC = () => { /** ページ遷移クリックイベント */ const onNextPageClick = () => { Router.push('/page2'); } /** レンダー部分 */ return ( <> <span>ここはページ1です。</span> <button onClick={onNextPageClick}>Page2に遷移します。</button> </> ) } export default Page1; page2.tsxも作成します。 import Router from 'next/router'; import React from 'react'; export const Page2: React.FC = () => { /** ページ遷移クリックイベント */ const onNextPageClick = () => { Router.push('/page1'); } /** レンダー部分 */ return ( <> <span>ここはページ2です。</span> <button onClick={onNextPageClick}>Page1に遷移します。</button> </> ) } export default Page2; 作成したら、 yarn dev を実行し、 http://localhost:3000/page1 にアクセスし動作確認を行いましょう。 React.FCやexport defaultなど謎なコードが存在しますが、 今のところは、呪文だと思って雰囲気をとらえておきましょう。 遷移部分の実装は、 Router.push('/page1'); ですね。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 - Part2 - Next.jsを使ってページ遷移を行う

目次 React入門 - Part1 - VsCodeをインストールしてReactでHello World React入門 - Part2 - Next.jsを使ってページ遷移を行う★★ここです★★ React入門 - Part3 - コンポーネントを書いてみる React入門 - Part4 - 親子間コンポーネントへの値の受け渡し Next.jsの概要 Next.js(ねくすとじぇーえす)とは、Reactの機能を拡張するためのJavaScriptフレームワークです。Next.jsを使用することで、サーバ側でページを生成するサーバサイドレンダリング(SSR)ができるようになります。サーバサイドレンダリングを行い、クライアント側のページ生成処理を減らすことで、より高速なウェブページの表示が可能になります。 ※ここからの抜粋 Next.jsはサーバーサイドレンダリング(SSR)やファイルベースルーティングなど多くの機能をゼロコンフィグで提供してくれます。 ※ここからの抜粋 Next.jsを設定する ここの「セットアップ」から「もう一度yarn devを実行すると・・・」まででOKです。 纏めます。 Git Bash等でプロジェクトを作成したいディレクトリに移動して以下を実行します。 npx create-next-app プロジェクト名 cd プロジェクト名 tsconfigを配置するために、以下のコマンドを実行します。 touch tsconfig.json yarn devを実行しますが以下のようにエラーになります。 何かが足りないので「yarn add・・・」を実行しなさいということなので、それに従い以下を実行します。 yarn add --dev typescript @types/react 再度、以下を実行します。 yarn dev ブラウザhttp://localhost:3000にアクセスすると以下の画面になります。 Next.jsでのページ遷移の概要 Next.jsはpages/ディレクトリに置いたフォルダ/ファイルの構成に従って、HTMLを生成してページ遷移を実現します。ルーティングライブラリは不要で、URLの構造に合わせてjs(ts)ファイルを配置するだけです。 以下の例ではindex.tsxが/、hoge.tsxが/hogeに対応付けられます。 --pages |--index.tsx -> / |--hoge.tsx -> /hoge |--fuga.tsx -> /fuga |--404.tsx |--_app.tsx -> アプリケーションエントリーポイント |--_document.tsx -> HTMLドキュメント構造記述用 Next.jsでページ遷移を試してみる pages配下にpage1.tsxを作成します。 import Router from 'next/router'; import React from 'react'; export const Page1: React.FC = () => { /** ページ遷移クリックイベント */ const onNextPageClick = () => { Router.push('/page2'); } /** レンダー部分 */ return ( <> <span>ここはページ1です。</span> <button onClick={onNextPageClick}>Page2に遷移します。</button> </> ) } export default Page1; page2.tsxも作成します。 import Router from 'next/router'; import React from 'react'; export const Page2: React.FC = () => { /** ページ遷移クリックイベント */ const onNextPageClick = () => { Router.push('/page1'); } /** レンダー部分 */ return ( <> <span>ここはページ2です。</span> <button onClick={onNextPageClick}>Page1に遷移します。</button> </> ) } export default Page2; 作成したら、 yarn dev を実行し、 http://localhost:3000/page1 にアクセスし動作確認を行いましょう。 React.FCやexport defaultなど謎なコードが存在しますが、 今のところは、呪文だと思って雰囲気をとらえておきましょう。 遷移部分の実装は、 Router.push('/page1'); ですね。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【TypeScript】PickとOmit

TypeScript 初心者です。 React の型定義で Pick と Omit について学習したのでまとめてみました。 Pick Pick<T, K> は既に存在する T 型の中から K で選択したプロパティのみを含んだ新たな型を構築する。 下記の例では、User型の name と email のみを含んだ UserItem型を作成している。 type User = { id: string; name: string; email: string; address: string; }; type UserItem = Pick<User, "name" | "email">; // | で複数指定可 const user: UserItem = { name: "hoge", email: "hoge@example.com", }; console.log(user); // => {name: "hoge", email: "hoge@example.com"} Omit Omit<T, K>は既に存在する T 型の中から K で選択したプロパティを除いた新たな型を構築する。 下記の例では、User から id と address を除いた UserItem型を作成している。 type User = { id: string; name: string; email: string; address: string; }; type UserItem = Omit<User, "id" | "address">; const user: UserItem = { name: "hoge", email: "fuga", }; console.log(user); //=> {name: "hoge", email: "hoge@example.com"}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む