20210508のReactに関する記事は10件です。

React + Docker + Typescript + ESlint + Prettier (初学者向け)

前提 dockerのインストール、ホスト環境へのnode.js,typescriptのインストールは完了しているものとする。エディタはvscodeを用いる。 はじめに 以下のようなディレクトリを作成 react/  ├ front/  ├ docker-compose.yml  ├ Dockerfile Dockerfileを編集 Dockerfile #LTSである偶数のnodeイメージを取ってくる FROM node:16.1.0-stretch-slim #ホストのfrontの中身をコンテナのフロントへコピーする。コンテナでのディレクトリも同時に作成される。 COPY ./front /front #作業ディレクトリの移動 WORKDIR /front #パッケージインストール RUN yarn install #コンテナのポートを指定 EXPOSE 3000 #コンテナスタート時に実行 ENTRYPOINT [ "yarn", "start" ] docker-compose.ymlを編集 docker-compose.yml version: "3.7" services: front: build: context: . dockerfile: Dockerfile volumes: #ホストのfrontの中をコンテナのfrontの中で共有 - ./front:/front working_dir: /front ports: #ホストの3000ポートをコンテナの3000ポートに繋ぐ - 3000:3000 create-react-appの実行 frontディレクトリの中に入り下記を実行 npx create-react-app . --template typescript frontの中にファイルが作られるので 次のコマンドを実行 yarn start そしてブラウザでlocalhost:3000を入力すればよく見る画面が現れる。 docker-composeの実行 reactディレクトリで docker-compose up を実行 ターミナルをもう一つ開き下記コマンドを実行すればコンテナに入ることができる。 docker-compose exec front bash ESlint Prettierの導入 保存したときにコード整形されるようにします VS Codeの拡張機能としてESlintをインストール Prettierもインストール 次に [code] > [基本設定] > [設定] の順序で設定画面を開きます。 検索ボックスに 『format』 と入力すると、 『Editor: Format On Save』 があるので、これにチェックを入れます。 そしてエディタ上で [Alt] + [Shift] + [F] を押すとフォーマッター(Prettier)を選択するように促されます。 それ以降保存でコードが整形されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CloudFormationでS3にSPA(React)を構築しよう

はじめに AWS CloudFormationを利用してS3静的ホスティング・SPA(React)構築のテンプレートのサンプルです。 テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。 コードはGitHubにもあります。 今回は、akane というシステムの dev 環境を想定しています。 同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。 ディレクトリ構成 akane (システム) ├── app (Reactアプリ) └── s3 (スタック) ├─ s3.yml (CFnテンプレート) └─ dev-parameters.json (dev 環境のパラメータ) AWS リソース構築内容 s3スタック s3バケット (akane-dev-s3-reacts) バケットポリシー (s3:GetObject) 実行環境の準備 AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。 Reactアプリ ビルド手順 予め node.js yarn をインストールする。 下記を実行してアプリをビルドする cd akane/app yarn install yarn build AWS リソース構築手順 下記を実行してスタックを作成 ./create_stacks.sh 下記を実行してスタックを削除 ./delete_stacks.sh 構築テンプレート 1. s3スタック s3.yml AWSTemplateFormatVersion: 2010-09-09 Description: S3 For Akane # Metadata: Parameters: SystemName: Type: String AllowedPattern: '[a-zA-Z0-9-]*' EnvType: Description: Environment type. Type: String AllowedValues: [all, dev, stg, prod] ConstraintDescription: must specify all, dev, stg, or prod. AppS3Bucket: Type: String AppIndexDocument: Type: String # Mappings # Conditions # Transform Resources: # S3 Bucket作成 akaneS3Bucket: Type: AWS::S3::Bucket Properties: AccessControl: Private BucketName: !Ref AppS3Bucket Tags: - Key: Name Value: !Ref AppS3Bucket - Key: SystemName Value: !Ref SystemName - Key: EnvType Value: !Ref EnvType WebsiteConfiguration: IndexDocument: !Ref AppIndexDocument # S3 BucketPolicy作成 akaneS3BucketPolicy: Type: AWS::S3::BucketPolicy DependsOn: akaneS3Bucket Properties: Bucket: !Ref akaneS3Bucket PolicyDocument: Statement: - Action: - s3:GetObject Effect: Allow Resource: !Join - '' - - 'arn:aws:s3:::' - !Ref akaneS3Bucket - /* Principal: AWS: '*' Outputs: akaneS3Bucket: Value: !Ref akaneS3Bucket Export: Name: !Ref AppS3Bucket all-parameters.json { "Parameters": [ { "ParameterKey": "SystemName", "ParameterValue": "akane" }, { "ParameterKey": "EnvType", "ParameterValue": "dev" }, { "ParameterKey": "AppS3Bucket", "ParameterValue": "akane-dev-s3-react" }, { "ParameterKey": "AppIndexDocument", "ParameterValue": "index.html" } ] } 3. 実行ファイル create_stacks.sh #!/bin/sh cd `dirname $0` SYSTEM_NAME=akane create_stack () { ENV_TYPE=$1 STACK_NAME=$2 aws cloudformation create-stack \ --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} \ --template-body file://./${SYSTEM_NAME}/${STACK_NAME}/${STACK_NAME}.yml \ --cli-input-json file://./${SYSTEM_NAME}/${STACK_NAME}/${ENV_TYPE}-parameters.json aws cloudformation wait stack-create-complete \ --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} } create_stack dev s3 ./akane/deploy_app.dev.sh exit 0 delete_stacks.sh #!/bin/sh cd `dirname $0` SYSTEM_NAME=akane delete_stack () { ENV_TYPE=$1 STACK_NAME=$2 aws cloudformation delete-stack \ --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} aws cloudformation wait stack-delete-complete \ --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} } ./akane/delete_app.dev.sh delete_stack dev s3 exit 0 4. デプロイファイル deploy_app.dev.sh #!/bin/sh cd `dirname $0` BASENAME=$(basename $0) FILENAME=${BASENAME%.*} ENV_TYPE=${FILENAME##*.} APP_DIR=app/build S3_DIR=s3/ BUCKET_NAME=$(cat ${S3_DIR}${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "AppS3Bucket").ParameterValue') aws s3 cp ${APP_DIR} s3://${BUCKET_NAME} --recursive exit 0 delete_app.dev.sh #!/bin/sh cd `dirname $0` BASENAME=$(basename $0) FILENAME=${BASENAME%.*} ENV_TYPE=${FILENAME##*.} S3_DIR=s3/ BUCKET_NAME=$(cat ${S3_DIR}${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "AppS3Bucket").ParameterValue') aws s3 rm s3://${BUCKET_NAME} --recursive exit 0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RESTサービスを作る・入門編

フロントエンドエンジニアは、WebAPIの「使う側」であります。 ですが、API、DataBaseも自分で手を動かして構築し、RESTサービスを包括的に経験する事で視野を広げようと思います。 本編は入門編なので、まずは概要をまとめました。 実際の開発はNode.js, Express, React, SQLite3を使い、続編で紹介します。 RESTとは: REST(REpresentational State Transfer)は、HTTPプロトコル作成者の一人であるRoy Fieldingによって2000年頃に提案され、複数のソフトウェアを連携させるのに適した設計原則です。クライアントとサーバを分離し、両者でデータを送受信するWebアプリケーションの開発に活用されている。 RESTful APIは、次の4つのRESTの原則に従って設計されたAPIとなります。 1. アドレス可能性 そのアプリケーションが提供する情報はURIを通して一意に(ユニークに)表現できること。 APIのバージョン、データを取得するのか、それとも更新するのか、などが一目でわかるようにすること。 2. ステートレス性 すべてのHTTPリクエストが完全に分離している性質であること。セッションなどの状態管理は行われないこと。有名なハンバーガーの注文がわかりやすいです。 3. 接続性 ある情報に「別の情報へのリンク」を含めることができること。そして、リンクを含めることで「別の情報に接続すること」ができること。 4. 統一インターフェース 情報の取得、作成、更新、削除といったリソースの操作は、すべてHTTPメソッドを利用すること。この場合のHTTPメソッドとは、取得「GET」、作成「POST」、更新「PUT」、削除「DELETE」を使用することで統一と認識のしやすさを図ることが出来ます。 RESTの大きな特長はHTTPの技術を効果的に活用したものであり、Web技術との親和性が高いからWebサービスやWebアプリケーションの開発に活用されている。 クライアントとサーバーの分離 RESTサービスではクライアントとサーバーを分離させて開発し、お互いのことを関与しないでよくなります。双方で疎通の決まりごと(リクエスト・レスポンスのインターフェース)を守っていれば、クライアントとサーバー個々で開発・改修が可能になります。 このクライアントサーバーモデルの利点はPC、スマホアプリと複数のプラットフォームから同じAPIを使う事が可能になる。 REST APIの設計: ①エンドポイント定義 エンドポイントはHTTPメソッドとURLの組み合わせです。 HTTPメソッドはこの4つが基本です GET 取得 POST 追加 PUT 更新 DELETE 削除 基本的に、この4つの処理でほとんどのシステム処理を完結させることができます。 URLをリソースと見なします。 HTTPメソッドとURLの組み合わせを、 何=リソース(URL) をどうする =HTTPメソッド と言う形式で定義します。 例を出しましょう。 GET: http://hogehoge.com/api/tickets 何を=チケット情報 どうする=GET / 取得 POST: http://hogehoge.com/api/tickets 何を=チケット情報 どうする=POST / 登録 URL設計で意識すべき点 ・URLにapiを含めてAPIとわかる様にする http\://hogehoge.com/api http\://api.hogehoge.com ・URLにAPIのバージョンを含める http\://api.hogehoge.com/v1.0/users/ ・URLに動詞は使わず、名詞の複数形だけで構成する user一覧 http\://api.hogehoge.com/v1.0/users user ID:1の情報を取得 http\://api.hogehoge.com/v1.0/users/1 users ID:1のphoto一覧取得 http\://api.hogehoge.com/v1.0/users/1/photos ・アプリケーションや言語に依存する拡張子は含めない URL内に.phpや.plなど言語に関係した拡張子を付けないようにする。悪意あるユーザーから脆弱性を突かれる要因にもなります。 ・リソースの構造が分かるようにする URLを見ただけで、リソースがどこに属しているか、どんなでーたをもってるかのデータ構造が分かるようにする。 このURLを見ると、userは写真のデータを持っている事がわかります。 http\://api.hogehoge.com/v1.0/users/1/photos ②リクエストおよびレスポンスのJSONの定義 これらは開発において、まずUIと仕様が決まり、それを基にどういうデータが必要か、ユーザーがどう使うのかも踏まえて、定義する必要があると思います(この作業は想像力が必要) 例えば、ログインページのAPIに何が必要なのか?を考えると。 POSTメソッドで、URLは/loginとし、emailとpasswordをサーバー側の認証に使うので、フロントはこれらをリクエストボディに含める必要があります。 リクエストにAPI側で必要なデータを渡し、レスポンスにはフロント側で欲しいデータを返してもらう。(場合によってはフェッチし直して、取り直すケースもある) まとめ 次は実際にNode.js, Express, React, SQLite3を使って簡単なRESTサービスを構築した時の学びを紹介したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React の開発&実行環境を docker-compose で構築する

shields.io でバッジ作るの楽しいよね はじめに React + TypeScript の開発及び実行環境を Docker (docker-compose) で構築するテンプレートリポジトリを GitHub で公開 動作確認環境 macOS Catalina : 10.15.7 docker desktop : 3.3.1(63152) Engine: 20.10.5 Compose: 1.29.0 Visual Studio Code : 1.56.0 GitHub テンプレートリポジトリ これまでは、GitHub で公開されている開発テンプレートを使うには、fork もしくはダウンロードしか方法がなかった (以下、公式の説明) 既存のリポジトリをテンプレートにして、自分や他のユーザが同じディレクトリ構造、ブランチ、およびファイルで新しいリポジトリを生成できるようにすることができます。 (中略) リポジトリをテンプレートにすると、リポジトリにアクセスできるユーザは誰でも、デフォルトブランチと同じディレクトリ構造とファイルで新しいリポジトリを生成できます。 (fork と比較すると)テンプレートリポジトリを利用することで、fork 元のコミットのログも残らず、テンプレートをベースに新しくリポジトリを作ることが可能になった (ダウンロードと比較すると)ダウンロード → git 初期化 → 再コミットの一連の手間を省略できる テンプレートリポジトリは下記の図のように Use this template に変更されている react-ts-dev 使い方 初回起動時は Docker Image のビルドが必要 コンテナを起動し、node_modules をセットアップ $ docker compose build $ docker compose run --rm frontend sh -c 'cd frontend && yarn install' 上記セットアップ完了後、実行環境を立ち上げる $ docker compose up 終了するときは $ docker compose down ブランチ内容 公開したテンプレートリポジトリのブランチは main, feature/material-ui-v4.11.1, feature/material-ui-v5.0.0-alpha.32 の 3 つ 【注意】node_modules/は git 管理の対象外のため、 git switchでブランチを切り替えると正しく動作しない場合があります main ブランチ React+TypeScript の開発及び実行環境 Visual Studio Code 上で開発を行うことを想定 下記のリポジトリをベースとした linter, formatter の機能付き 実行結果 feature/material-ui-v4.11.1 main ブランチに加えて material-ui ライブラリを追加 実行結果 下記の図のように material-ui の Button が表示されていれば成功 feature/material-ui-v5.0.0-alpha.32 main ブランチに加えて material-ui のプレビュー版(ver.5) ライブラリを追加 実行結果 下記の図のように material-ui の Button が表示されていれば成功 おわりに backend 何か作るか・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React/Material-UIのCardでカウントアップ

react-countupで実装。 ./src/App.js import logo from "./logo.svg"; import "./App.css"; import { Card, CardContent, Grid, Typography } from "@material-ui/core"; import LocalHospitalIcon from "@material-ui/icons/LocalHospital"; import CloseIcon from "@material-ui/icons/Close"; import DirectionsRunIcon from "@material-ui/icons/DirectionsRun"; import FavoriteIcon from "@material-ui/icons/Favorite"; import CountUp from "react-countup"; function App() { return ( <div> <Grid container spacing={1}> <Grid item xs={3} color="primary"> <Card style={{ backgroundColor: "#0095d9" }}> <CardContent> <Typography variant="h5"> <CountUp start={622273 - 1000} end={622273} separator="," /> </Typography> <Typography> <LocalHospitalIcon /> Confirmed </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#e60033" }}> <CardContent> <Typography variant="h5"> <CountUp start={10566 - 1000} end={10566} separator="," /> </Typography> <Typography> <CloseIcon /> Deaths </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#3eb370" }}> <CardContent> <Typography variant="h5"> <CountUp start={542413 - 1000} end={542413} separator="," /> </Typography> <Typography> <DirectionsRunIcon /> Recovered </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#ffd900" }}> <CardContent> <Typography variant="h5"> <CountUp start={69294 - 1000} end={69294} separator="," /> </Typography> <Typography> <FavoriteIcon /> Active </Typography> </CardContent> </Card> </Grid> </Grid> </div> ); } export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React/Material-UIでCard

./src/App.js import logo from "./logo.svg"; import "./App.css"; import { Card, CardContent, Grid, Typography } from "@material-ui/core"; import LocalHospitalIcon from "@material-ui/icons/LocalHospital"; import CloseIcon from "@material-ui/icons/Close"; import DirectionsRunIcon from "@material-ui/icons/DirectionsRun"; import FavoriteIcon from "@material-ui/icons/Favorite"; function App() { return ( <div> <Grid container spacing={1}> <Grid item xs={3} color="primary"> <Card style={{ backgroundColor: "#0095d9" }}> <CardContent> <Typography variant="h5">622,273</Typography> <Typography> <LocalHospitalIcon /> Confirmed </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#e60033" }}> <CardContent> <Typography variant="h5">10,566</Typography> <Typography> <CloseIcon /> Deaths </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#3eb370" }}> <CardContent> <Typography variant="h5">542,413</Typography> <Typography> <DirectionsRunIcon /> Recovered </Typography> </CardContent> </Card> </Grid> <Grid item xs={3}> <Card style={{ backgroundColor: "#ffd900" }}> <CardContent> <Typography variant="h5">69,294</Typography> <Typography> <FavoriteIcon /> Active </Typography> </CardContent> </Card> </Grid> </Grid> </div> ); } export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Drag&Drop UI | Dropする前のファイルのtype属性を知りたい

はじめに ドラッグ&ドロップのUIを作っている際に、Drop前のファイルのtype属性を知りたい要件があったので、ニッチな内容ですが、忘備録としてメモしていきます。 ※ 以下のコードは、Reactとreact-dropzoneを使用しています。 前提 ここに簡単なDropZoneUIのサンプルコードを示します。 onDropがdropZoneにファイルをドロップした時に呼ばれる処理で、onDragOverがファイルを掴んだ状態で、dropZoneにオンマウスした際に呼ばれる処理です。 DropZone/index.tsx import React, { CSSProperties, useCallback, useMemo } from 'react'; import { useDropzone } from 'react-dropzone'; const baseStyle = { width: '70%', display: 'flex', flexDirection: 'column' as const, alignItems: 'center', padding: '20px', borderRadius: 4, borderColor: 'rgb(216 216 216)', borderStyle: 'solid', backgroundColor: 'rgb(202 182 143 / 10%)', color: 'rgb(158 158 158)', outline: 'none', }; export const Dropzone = () => { const onDrop = useCallback((acceptedFiles) => { console.log(acceptedFiles); }, []); const onDragOver = useCallback((acceptedFiles) => { console.log(acceptedFiles); }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, onDragOver, }); const style: CSSProperties = useMemo( () => ({ ...baseStyle, }), [] ); return ( <div style={{ display: 'flex' }}> <div {...getRootProps({ style })}> <input {...getInputProps()} /> {isDragActive ? ( <p>Drop the files here ...</p> ) : ( <p>Drag 'n' drop some files here, or click to select files</p> )} </div> </div> ); }; イメージの補足として以下GIFを示します。 onDropが呼ばれた時は、Fileオブジェクトからファイルの情報を知ることが出来ます。 onDragOverが呼ばれた時に、対象のファイルのtypeが知りたい時にどうやって知るのでしょうか? ドロップ前のファイルのtype属性を知る方法は? [A] DataTransferオブジェクトを使用する。 以下に変更箇所の実装を示します。 DropZone/index.tsx(onDragOverメソッドを変更) // DropZoneにOnMouseした際に呼ばれる const onDragOver = useCallback((event) => { event.stopPropagation(); event.preventDefault(); console.log(event.dataTransfer.items[0]); const fileType = event.dataTransfer.items[0].type; console.log(fileType); }, []); event.dataTransfer.items[0].type でファイルのタイプを取得することができます。 consoleのキャプチャは以下の通り。アップロードしようとしているファイルのtype属性が取得できているのが分かります。 DataTransferオブジェクトとは? ここで使われているDataTransferとは何でしょうか? これはドラッグ&ドロップ操作中にドラッグされているデータを保持するために使用されるもので、1つ以上のデータ項目を保持することができ、それぞれが 1つ以上のデータ型を持っています。 MDNの解説が詳しいので、最後に関連リンクを貼っておきます。 MDN | DataTransfer MDN | DragEvent.dataTransfer MDN | DataTransfer.types
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】モジュール[ES2015] import/export をちゃんと理解する

モジュール [ES2015]import/export export defaultって何??、なんでこのimportはimport {App} from './src/App.js'って書くのに、こっちはimport hoge from './src/App.js'なの?とか不思議に思ったことはありませんか? (私はあります? ) みんな大好きReactを書く時に、いつも書いているimport exportをちゃんと理解してちゃんと使うべきなので、メモとして残します。 ES2015で登場したJavaScriptのモジュールの機能により、外部のリソースをインポートして利用できるようになりました。 (念のためですが、モジュールはReactの機能ではありません) インポートを利用する前提として、別ファイルで利用したい変数や関数などをあらかじめエクスポートしておく必要があります。 モジュールの機能により、便利な変数や関数を再利用できる、モジュールごとにスコープがあることでグローバル変数名に対して上書きしてしまうリスクを回避できるなど、コードの保守性が高まリます。 Reactでは、モジュールのエクスポートとインポート機能を利用してコンポーネント間における変数や関数のやりとりをします。 エクスポートとインポートには名前付きとデフォルトがあります。 名前付きexportとimport 名前付きエクスポートとインポートについて見ていきましょう。 名前付きexport 別ファイルからアクセスできるようにするため、名前を付けて作成した変数や関数を名前付きエクスポートします。 // 変数 sampleVariable を 宣言 const sampleVariable = "sampleVariable" // 関数 sampleFunc を 宣言 const sampleFunc = () => console.log('Hello,React!'); // 作成した変数や関数をエクスポートすることで、別ファイルにてインポートできるようになる export {sampleVariable, sampleFunc} 名前付きエクスポートでは、作成した変数や関数の前にexportを付けることで、作成時の宣言と同時にエクスポートすることもできます。 // 変数 sampleVariable を 宣言と同時にエクスポート export const sampleVariable = "sampleVariable" // 関数 sampleFunc を 宣言と同時にエクスポート export const sampleFunc = () => console.log('Hello, Vue.js!'); 名前付きimport 次に名前付きエクスポートされた関数や変数を、名前付きインポートします。 namedExportModule.jsからエクスポートされている変数sampleVariableと関数sampleFuncをインポートするには次のように記述します。 import { sampleVariable, sampleFunc } from "./namedExportModule.js"; // 呼び出し console.log(sampleVariable); // sampleVariable console.log(sampleFunc); // sampleFunc() {} console.log(sampleFunc()); // Hello,React! 名前付きexportとimportのエイリアス エイリアスの仕組みを利用することで、宣言済の変数や関数に宣言時と異なる名前を指定してインポートすることができます。エイリアスを付けるためにはasの後に別の名前を指定します。 namedExportModule.jsからエクスポートされている変数sampleVariableをmyVariable、sampleFuncをmyFuncという名前でインポートするには次のように記述します。 import { sampleVariable as myVariable, sampleFunc as myFunc } from "./namedExportModule.js"; // 呼び出し console.log(myVariable); // sampleVariable console.log(myFunc); // sampleFunc() {} console.log(myFunc()); // Hello,React! 名前つきエクスポートをmyModuleオブジェクトとしてまとめてインポートすることもできます。namedExportModule.jsから、MyModuleという名前でインポート、myModule.sampleVariable、myModule.sampleFuncと書いて呼び出します。 // 名前つきエクスポートをmyModuleオブジェクトとしてまとめてインポート // import の後の {} は不要 import * as myModule from "./namedExportModule.js"; // 呼び出し console.log(myModule.sampleVariable); // sampleVariable console.log(myModule.sampleFunc); // sampleFunc() {} console.log(myModule.sampleFunc()); // Hello,React! デフォルトexportとimport デフォルトエクスポートとインポートについて見ていきましょう。 デフォルトexport デフォルトエクスポートは、モジュールごとに1つだけエクスポートできる特殊なエクスポートです。 次のコード例では、関数の作成と同時にデフォルトエクスポートしています。 // defaultエクスポートは、モジュールごとに1つだけエクスポートできる export default function App(){ console.log("Hello,React!"); } export defaultは、ファイルごとに1つだけエクスポートするため、インポートする側も何をインポートすればよいか明確です。そのため関数の作成と同時にデフォルトエクスポートする場合、関数名は省略可能です。 // defaultエクスポートは、モジュールごとに1つだけエクスポートできる // 関数名は省略できる export default function () { console.log("Hello,React!"); } 複数の変数を同時に作成できる変数については、変数宣言と同時にデフォルトエクスポートすることはできないので注意しましょう。 デフォルトimport 名前付きインポートの場合にはimportの後に{}が必要ですが、デフォルトインポートの場合には不要です。 また名前付きエクスポートの場合、指定したモジュールから同じ変数名や関数名を指定してインポートしますが、デフォルトエクスポートの場合、任意の名前をつけてインポートします。 // デフォルトエクスポートされたモジュールをインポートする場合、import {App} ではなく import App // App は任意の名前 import App from './defaultExportModule.js' console.log(App) // App() {} console.log(App()) // Hello,React! 再エクスポート 再エクスポートとは、1度インポートしたモジュールを別ファイルが利用できるようにエクスポートし直すことです。export ... from ...構文を利用して、インポートした直後にエクスポートできます。 再エクスポートは、複数のモジュールからエクスポートされたものをまとめたモジュールを作るときなどに利用されます。 // namedExportModule.js から sampleVariable と sampleFunc をインポートした直後に再エクスポート export { sampleVariable, sampleFunc } from "./namedExportModule.js"; // namedExportModule.js から sampleVariable を myVariable、 sampleFunc を myFunc としてインポートした直後に再エクスポート export { sampleVariable as myVariable, sampleFunc as myFunc } from "./namedExportModule.js"; // 名前つきエクスポートを myModule オブジェクトとしてまとめてインポート import * as myModule from "./namedExportModule.js"; // myModule オブジェクトを再エクスポート export {myModule} // 上記の2行を1行で記述できるようになった[ES2020] // myModule オブジェクトを再エクスポート export * as myModule from "./namedExportModule.js"; HTMLファイルからモジュールを呼び出す HTMLファイルからモジュールを利用しているJavaScriptファイルを呼び出す場合、type="module" 属性が必要です。 html :index.html <script type="module" src="./modules/importModule.js"></script> JavaScriptの基本をしっかり固めて、ますます楽しいReactライフを!!? 参考サイト: - JavaScript Primer - MDN Web Dogs
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】画像の変更を動的に反映させるのに苦労した

React × Rails SPAの作成時に画像の変更を動的に反映させるのに苦労したのでアウトプット やりたいこと 以下のようにモーダル内のフォームからニックネームおよびアバター画像を変更し、非同期的にマイページにも反映させたい。 フォーム送信では以下のようにaxiosを用いてAPIと通信した後、React-routerのnavigationを用いてマイページに遷移させている。 formSubmit(e) { e.preventDefault() if (this.props.location.state.content == 'Edit Profile') { axios .patch('/api/v1/users', {user: {nickname: this.state.user.nickname, avatar: this.state.avatar }} ) .then(response => { this.props.history.push('/mypage') // this.props.history.push('/mypage') return response }) 遷移先では componentDidMount() { axios .get('/api/v1/mypage') .then(response => { if (response.data.avatar) { this.setState({ user: response.data.user, books: response.data.books, avatar: response.data.avatar }) } else { this.setState({ user: response.data.user, books: response.data.books, avatar: Sample }) } return response }) } のようにcomponentDidMountを用いてAPIと通信しているので ユーザー情報の更新→history.push→componentDidMount→画面に変更内容が反映 と処理が進む想定。 しかしこれでは画像のみ非同期で変わらなかった。なんで??? 試したこと history.push時に値を遷移先に送る formSubmit(e) { e.preventDefault() if (this.props.location.state.content == 'Edit Profile') { axios .patch('/api/v1/users', {user: {nickname: this.state.user.nickname, avatar: this.state.avatar }} ) .then(response => { this.props.history.push({pathname: '/mypage', state: {user: response.data.user, avatar: response.data.avatar}}) // this.props.history.push('/mypage') return response }) こんな感じでpushの引数にstateとして渡すことで遷移先で取得できないか試してみた。 しかしこれではだめだった。 ターミナルのログを見る→そもそもcomponentDidMountが動いていない? ターミナルのログを見る限りそもそもcomponentDidMountが動いていなさそう?と気づく。 history.pushでcomponentにマウントしているつもりだったけどモーダルだからコンポーネントにはすでにmountしている扱いなのかな。 componentDidUpdateでsetStateを用いる history.pushで送った値は this.props.location.state から取り出せるのでこれを使ってcomponentDidUpdateを使えばいいんじゃないか?と気づく。 componentDidUpdate() { let updatedProps = this.props.location.state if (updatedProps) { if (updatedProps.avatar) { this.setState({ avatar: updateProps.avatar }) } } } しかしこの場合 Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. というエラーが出てしまう。 調べた限りcomponentDidUpdateでsetStateをすると画面が再描画されるのでそのときにまたcomponentDidUpdateが呼び出されて無限ループに入っている?ような感じとみた。 (参考:https://bit.ly/3hclKYS) componentDidUpdateでsetStateを使わずにstateに値を代入する setStateがダメならstateに値を代入すればいいじゃんってことで componentDidUpdate() { let updatedProps = this.props.location.state if (updatedProps) { if (updatedProps.avatar) { this.state.avatar = updatedProps.avatar } } } このように記述した。するとstateは変わったが画像自体に変化はなかった。 画像のsrc属性を直接触ってみる いろいろ調べてみると画像を動的にjsで変化させる際にsrc属性に値を代入する、ということをしている記事をよく見かけたのでそれをやってみることに。 componentDidUpdate() { let updatedProps = this.props.location.state if (updatedProps) { if (updatedProps.avatar) { this.state.avatar = updatedProps.avatar document.getElementById('avatar').src = this.state.avatar } } } avatar、はアバター画像が入っているimgタグのid。 これで無事画像そのものも非同期で変更できた。 感想 ちょっとしたフロントの実装だと思っていましたが思ったより細かいところで躓いてしまった。 Reactのライフサイクルとかもっと勉強しないとなーという気持ちになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

reacthooksを利用して楽天APIで簡単ホテル検索

apiを利用したことがなかったので、勉強のために実装したので備忘録 開発環境 macOSX Catalina バージョン10.15.7 VisualStudioCode ディズニーという文字が入ったホテルのリストを取得して、ホテル名とアクセスを表示する Promiseオブジェクトが - awaitとすることでjsonとしてデータ取得ができる。 - asyncにしている関数内ではないとawaitは使用できない - asyncは関数の定義に含めなければならない(非同期処理の指定だから) - useEffect内で関数を実行することにより、任意の関数内でasyncを実行できる 上記の情報を考慮して記述 import './App.css'; import axios from 'axios'; import { useEffect, useState } from 'react'; const getHotels = () => { const q = "ディズニー" return axios.get(`https://app.rakuten.co.jp/services/api/Travel/KeywordHotelSearch/XXXXXXXXXXXXXX?&applicationId=XXXXXXXXXXXXXXXXX&format=json&keyword=${q}`) } const App =()=> { const [hotels, setHotels] = useState([]) useEffect(async () => { const res = await getHotels() const hotels = res.data setHotels(hotels.hotels) }, []) // mapがうまくいかない仮説:配列になってない console.log(hotels) // console.log(hotels[0].hotel[0]) return ( <div className="App"> {hotels.length > 1 && // mapする時にkeyidを入れることで、後から指定してスタイル変更などができなくなる hotels.map((hotel, id)=>{ console.log(hotel.hotel[0]) return( <div className="hotel" key={id}> <div className="hotel-name">{hotel.hotel[0].hotelBasicInfo["hotelName"]}</div> <div className="hotel-access">{hotel.hotel[0].hotelBasicInfo["access"]}</div> </div> ) }) } </div> ); } export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む