20210829のReactに関する記事は12件です。

React 入門道場 ~JSX 基礎文法~

はじめに Reactを学習していてJSXについて理解が浅かったので今回簡単ににまとめてみました。 JSXって、そもそも何なのと思った人はこちらから参照ください。 JSX基礎文法 壱 ①Reactライブラリをimportする import React from 'react'; ②return文の中がJSX構文 import React from 'react'; const BlueButton = () => { return ( <button className={'btn-blue'}> Click me! </button> ) } export default BlueButton; 基本的にはHTMLと同じ構文。class要素はclassNameに変わる JSXの見た目はHTMLですが、実際Javascriptで注意が必要!!!! classと記載してしまうとオブジェクトのclassと認識してしまうので、明示的にclassNameと分けるためにclassNameとついている。その後にクラス命を記載する必要がある。 JSX基礎文法 弍 import React from 'react'; const Thumbnail = () => { const caption = 'sample写真' const imagePath = '/img/sample.png' return (         <div> <p>{caption}</p> <img src={samplePath} alt={'sample写真'} /> </div> ) } export default Thumbnail; 1.キャメルケースで記述 imagePath 2.{}で変数を扱える {}を使うとJavascriptの世界だと明示的に使用することができる。 const caption = 'sample写真' Pタグで{caption}を使うと最終的な出力として「sample写真」の文章が出力される。 同様に、 <img src={}にsamplePathを渡すと/img/sample.pngが出力されます。 { }をJSXの世界になる 3.閉じタグが必要 /> 閉じタグがないとエラーになるので記載するようにしましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】JSX attributes must only be assigned a non-empty expressionの対処法

症状 Reactでコンポーネントを作成していた時、以下のエラーが発生しました。 翻訳すると、「JSX属性には、空でない式のみを割り当てる必要があります」でした。 error SyntaxError: 絶対パス JSX attributes must only be assigned a non-empty expression 該当のソースは以下です。 Hoge.jsx export function Hoge () {  function submitHandle() { return "Hoge" } return ( <buttom onClick={}></buttom> ) } 解決方法 buttomの中のonClickイベントの中身を指定することで解決しました。 エラー内容そのままで、jsx要素であるreturn内部のbuttomに何も指定されていなかったため、エラーが出ていたようです。 Hoge.jsx export function Hoge () {  function submitHandle() { return "Hoge" } return ( <buttom onClick={() => submitHandle}></buttom> ) } ​
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】React、Flux、Redux、Webpack、SSRについて

職場でReactを使ってフロントエンドを構築していますが、サービスの注文履歴画面にエラーが発生している模様。 まずQAリストを作成し、それに則ってエラーの解消をしていきたいところですが、 そもそもReact周りの知識がないことで、コードのどこの部分を検証すれば良いのかさえも分からなかったため、 一つ一つ理解していくための自分用メモです。 (上司によると、フロントではstoreやview周りをチェックする必要があるとのこと。)  Flux Flux(フラックス)はFacebook社が提唱している、クライアントサイドのWebアプリケーション開発のためのアプリケーション・アーキテクチャ(設計思想)です。単方向のデータフローを構築できることが最大の特徴で、開発の規模が大きくなってもデータの流れを見失いづらいことが大きなメリットです。Reactとの併用を主に想定して生み出されたため、ReactによるWebアプリケーション開発を行う多くの現場で採用されています。 名前 役割 Store(ストア) アプリケーションの状態データを保持するオブジェクト、状態の更新を実施する処理 Action(アクション) 状態を更新するための指示内容を表すメッセージ Dispatcher(ディスパッチャー) Storeに対してActionによる更新指示を行う関数 Fluxの最大の特徴「単方向データフロー」 状態の更新指示内容である「Action」を関数「Dispatcher」で「Store」に伝えて状態を更新し、その結果を「View(React)」に伝える、といった流れです。 実際にアプリに組み込んだ場合には、ユーザーが画面を操作したイベントに応じて新たなActionを発行(dispatch)して、状態と画面を更新していく形になるので、次の通りになります。  上図に沿って初期化後の処理を一つひとつ見ていくと、次のことをしています。 1.ユーザーがViewを操作する(ボタンを押す、文字を入力する、など) 2.更新したい事柄をActionの形にまとめて、Dispatcherに渡す 3.DispatcherからActionを渡されたStoreが、Actionの内容に応じて状態を更新する 4.Storeの状態が更新されたことを検知したViewが書き換わる Fluxアーキテクチャを採用したアプリは、このデータフローを繰り返していくことになります。データの流れを逆流させたり、Dispatcher以外の方法でStoreを更新したりすることはできないので、アプリの動きを把握しやすいです。Fluxが開発規模の拡大に強いとされる理由はここにあります。  引用  アプリの状態管理を安全に行うためのFluxとRedux ▼こちらも参考になります。 Fluxとはなんなのか  Redux Reduxは、Reactが扱うUIのstate(状態)を管理をするためのフレームワークのこと。 React以外にもAngularJSやjQueryなどと併せて使用することもできるが、Reactと使用するのが一番相性がいい。 React→stateの管理するデータフローにFluxを提案 Redux→Fluxの概念を拡張してより扱いやすく設計 ▼こちらも参考になります。 [フロントエンド] Reduxの考え方をシンプルに理解しよう(入門記事) FluxをReactとReduxを用いたショップアプリに落とし込んで考えてみる Container ショップ。接続されたコンポーネント。 Action ショップスタッフ。例えば在庫が減ってきたら管理人に「在庫の補充をお願いします」と変更(Action)を依頼(Dispatch)する人。 Reducer 管理人、マネージャー。ショップスタッフ「Action」からの依頼を受けて、それを許可する人。 Store 全ての状態を一元管理する「倉庫」 管理人「Reducer」の指示によってStoreからまた新しい商品がショップに並べられる。 mapStateToProps、mapDispatchToProps 新しく発行された状態をコンテナに接続する関数 WebpackとSSRについて こちらの記事を読みましたが、ザックリとしかまだ読めていません。 これで全体像はなんとなく分かった気がしますが、正直浅い理解です。。。 まずはとにかくまずはインプットしていかないないとと思い、 Udemyのこちらの教材が良いという情報があったため購入してみました。 英語ですが翻訳しながら理解していきたいと思います。 Udemy:Modern React with Redux 早くコードが書けるようになりたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを基本からまとめてみた【6】【State(ステート)】

はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、卒業後、フロントエンドのエンジニアを目指す事に。 Reactの学習した事を言語化し、認識の深化による備忘録として記載。 Stateとは コンポーネント単位で状態を保持する為の仕組み。 『状態』とは、数値、文字列、配列などWebアプリケーションを表すデータの事。 Propsとの違い コンポーネント単位で状態を保持。Stateだと親子関係なくそれぞれ独立した仕組み。 Propsの場合、親コンポーネントから子コンポーネントへ値を渡す仕組み。 Stateの場合、一度定義したものは後々変更する事ができる。 StateとPropsの違い Props  親コンポーネントから子コンポーネントへ値を渡す仕組み 親コンポーネント:Post.js Post.js <Button title="Post"/>   ➡️  親から子へ ⬇️ 子コンポーネント:Button.js Button.js <button class=" "> <I class= "..."/> {props.title} ⬅️ 親から子へ ⬅️ </button> ➡︎ 『Post』変更不可 State コンポーネント単位で状態を保持する仕組み 親コンポーネント:App.js App.js state={admin: trune} ⬅️お互い独立 子コンポーネント:Counter.js Counter.js state={count: 0} ⬅️お互い独立 icrement=()=> { count++ } ➡︎ 『count: 5』変更可能 React.Fragmentとは React でよくあるパターンの 1 つに、コンポーネントが複数の要素を返すというものがある。フラグメント (fragment) を使うことで、DOM に余分なノードを追加することなく子要素をまとめることができるようになる。 sample.js render() { return ( <React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> ); } 実際にコーディングを行う (1) 作成するアプリのイメージ共有 count:0 が表示されている。『Increment』ボタンを押したら増え、 『Decrement』ボタンを押したら減るアプリ。 (2) counterプロジェクトを新規作成 terminal creat-react-app <プロジェクト名> (3) index.jsとApp.jsを作成する デフォルトのファイルを全部削除して、index.jsとApp.jsを作成する。 index.js import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; ReactDOM.render(<App />, document.querySelector("#root")); App.js import React from "react"; const App = () => { return ( <div> <div>App</div> </div> ); }; export default App; (4)semantic-ui-cdnを導入する https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css 上記のコードを piblic/index.html に貼り付ける。 (5) Counter.jsを作成する ① src/components / Counter.jsを作成する。 ② React.Fragmentを使用する。 Counter.js import React, { useState } from "react"; const Counter = () => { const [count, setCount] = useState(0); const onCountUp = () => { setCount(count + 1); }; const onCountDown = () => { setCount(count - 1); }; return ( <React.Fragment> <p>Count: {count}</p> <button className="ui primary button" onClick={onCountUp}> Increment </button> <button className="ui red button" onClick={onCountDown}> Decrement </button> </React.Fragment> ); }; export default Counter; (6) App.jsを作成する App.js import React from "react"; import Counter from "./components/Counter"; const App = () => { return ( <div className="ui container" style={{ marginTop: "20px" }}> <Counter /> </div> ); }; export default App; 参考サイト 【React入門】#6 State(ステート)の理解
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerのReact環境構築を詳細に解説する

なぜこの記事を書いたのか 「Dockerfile と docker-compose.ymlを一から作成してビルドする」一連の流れをこれまでにしたことがなかったので、学習がてら環境構築をして、この記事にもアウトプットすることにした。 環境構築完了(ゴール)の確認 この記事のゴールは以下のようにReactのページが起動すること。 環境構築の手順 ①Docker、nodeのインストール ②Dockerfile、docker-compose.ymlの作成 ③ビルド ④Reactアプリの作成 ⑤コンテナの実行 ①Docker、nodeのインストール Dockerのインストール https://www.docker.com nodeのインストール https://nodejs.org/ja/download/ ②Dockerfile、docker-compose.ymlの作成 docker-react-app(プロジェクトフォルダ)  └┬─ Dockerfile   └─ docker-compose.ymlの 上記のフォルダ構成で、フォルダとファイルを任意の場所へ作成する。 Dockerfileの作成 FROM node:14.17.5 WORKDIR /usr/src/app/ node:〇〇の”〇〇”は「node --version」でnodeバージョンを確認して記述する。上記でnodeをインストールしていれば、バージョンを取得できる。 docker-compose.ymlの作成 docker-compose.yml version: '3' services: node: build: context: . tty: true environment: - NODE_ENV=development volumes: - ./:/usr/src/app command: sh -c "cd reactapp && npm start" ports: - "3000:3000" ③ビルド docker-compose build ④Reactアプリの作成 docker-compose run --rm node sh -c "npm install -g create-react-app && create-react-app reactapp" ⑤コンテナの実行 docker-compose up ここまで完了したらWebブラウザで「 http://localhost:3000 」へアクセスする。以下のようにReactのページが起動すれば環境構築完了となる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter経験者がReactで温泉ランキングを作ったその1(API取得まで)

React歴 もともとwebの知識(html/css,javascript)とFlutterでのアプリ開発経験があるので、行けるのではないかと思い挑戦しました。 つまり、Reactは初です。 必要な知識 html/cssとFlutterが理解できれば大丈夫です。 完成図 検索フォーム(今回は昨日少なめな検索フォームです)があり、下に内容がでるという感じです。 参考にした動画 こちらの動画を参考にさせていただきました。 https://www.youtube.com/watch?v=U9T6YkEDkMo&t=2344s 環境構築 こちらを参考にしました。 https://qiita.com/rspmharada7645/items/25c496aee87973bcc7a5 すごく簡単でした。 App.js こちらがApp.jsですね。 Flutterでいうところのmain.dartいう認識でよさそう。 App.js import React from 'react'; const App = () => { return ( <div id="root"> </div> ); } export default App; index.js Reactに必要なモノをimportしています。 ちなみに{useState, useEffect}は今のところ気にしなくてもいいです。 後で使うメソッドのようなものをimportしています。 index.js import React, {useState, useEffect} from 'react'; import ReactDOM from 'react-dom'; import './index.css'; APIを探してみる じゃらんwebサービスが使えないみたいですね。。 なので今回はこちら。 アプリID発行からログインすると新規アプリが作成できます。 アプリIDは後で利用します。 今回は以上です。 次回は実際にコードを書いていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

reactのfragmentについて

本記事について 本記事はReact.Fragmentに関して書いた記事です。筆者の備忘録的な位置付けも兼ねており、間違い等あるかもしれませんが、何かありましたら、お手数ですがコメント等でご連絡いただけますと幸いです。 Fragmentとは シンタックスシュガーのこと。通常、Reactコンポーネントはルートの要素が一つでないといけない。returnでは一つの要素しか返せないが、Fragementを使用することでその制約を外すことができる。 わかったようなわからないような,,, 公式の例 例えば、以下のようなコードがあるとする。 Tableクラスでは、Columnsコンポーネントを呼び出している。 class Table extends React.Component { render() { return ( <table> <tr> <Columns />?ここで呼び出してる </tr> </table> ); } } こちらは先ほど呼び出されたColumnsコンポーネント。 returnの中には、div要素で囲われた一つの要素がreturnされている。div要素の中にtd要素が入っているイメージ。 上に書いたように、reactコンポーネントでは一つの要素をreturnするため、 class Columns extends React.Component { render() { return ( <div>?これがないと、二つの要素をreturnしようとしてしまうので、あえて`div要素`で囲うことで要素を一つにまとめている。 <td>Hello</td> <td>World</td> </div> ); } } 最終的なイメージとしては以下のコードが返ってきている。 <table> <tr> <div> <td>Hello</td> <td>World</td> </div> </tr> </table> ここで、< div ></ div >タグで囲わずにReact.Fragmentを使用するとColumnsコンポーネントは以下のような記述となる。 class Columns extends React.Component { render() { return ( <>?React.Fragmentの代用。(<React.Fragment>でもよい。) <td>Hello</td> <td>World</td> </> ); } } すると最終的に以下のコードが返ってくるイメージ。 <table> <tr> <td>Hello</td> <td>World</td> </tr> </table> 余計ながないため、デザイン崩れ等の対策となるようです。 < React.Fragment >と< div>< /div>で要素を囲った時の違い 前者ではkeyを持つことができる。現時点では、フラグメントに渡せる唯一の属性のよう。 将来的にはもっと増えるかも...? key はフラグメントに渡すことができる唯一の属性です。将来的には、イベントハンドラのような他の属性を渡すこともサポートするかもしれません。 とのこと。 結局、デザインのこととか考えるとfragmentを使用した方がいいのか???書くのも楽そうだし... < React.Fragment >と< div>< /div>で要素を囲った時の違い2 ・単純に< React.fragment >を使用する方が時間がかかるようです。 スタイルを当てないからdivタグ使わずにReactFragment使っちゃえ〜、という考え方は安直なのかもしれない... 結論 結局、時間がかかってもスタイル崩れや余計な要素までreturnしてしまうことを考えればReactFragmentを利用するのが無難なのかもしれないが、どういった仕組みなのかは理解しておいた方がいいだろう、というふわっとした結論に行き着いてしまった。。。 参考 公式ドキュメント https://ja.reactjs.org/docs/fragments.html#short-syntax こちら、fragmentと タグを使用した際の比較をまとめてくれている記事です。 ありがとうございます。。。 https://zenn.dev/januswel/articles/c80ac055b72955d71d41
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による初心者のためのGatsbyJS覚書6(メタデータの設定)

この記事について 会社の業務でGatsbyJSを少しだけさわった経験がある新卒2年目が作成しています。 参考資料として書籍を使用していますが、筆者がビギナークラスのため読んでいて「?」となる部分や間違っている箇所もあるかと思います。 参考までに、そして間違えている箇所がありましたらご連絡いただけると嬉しいです。 Chapter6:メタデータの設定 備忘録メモ メタデータの設定方法 必要なパッケージをインストール seo.jsを作成して仮データが表示されるか確認する。 サイト全体で用意するデータを用意してクエリとして取得 ページごとのメタデータの値を設定する ページのURLやOGP(Open Graph Protocol)を追加する PWA対応用のパッケージを用意する 必要なパッケージをインストール npm install gatsby-plugin-react-helmet react-helmet ※gatsby-config.jsにもgatsby-plugin-react-helmetプラグインとして記載しておく。 seo.jsを作成して仮データが表示されるか確認する。 seo.jsファイルを作成 import React from "react" import { Helmet } from "react-helmet" const Seo = () => ( <Helmet> <html lang="言語の種類" /> <title>タイトル</title> <meta name="description" content="説明" /> </Helmet> ) export default SEO ひとまずindex.jsで呼び出して表示されるか確かめてみる。 import Seo from "../components/seo" const IndexPage = () => ( <Layout> <Seo /> <section className="hero"> ディベロッパーツールで確認 サイト全体で用意するデータを用意してクエリとして取得 gatsby-config.jsにサイト全体で使用するデータを追加する。 module.exports = { /* Your site config here */ siteMetadata:{ title: `ESSENTIALS`, description: `美味しい食材と食事を探求するサイト`, lang: `ja`, }, plugins: [ `gatsby-plugin-image`, ... サーバーを再起動するとGraphQLにデータが追加させるのでクエリを取得してseo.jsへ記載 const Seo = () => { const data = useStaticQuery(graphql` query { site { siteMetadata { title lang description } } } `) return( <Helmet> <html lang={data.site.siteMetadata.lang} /> <title>{data.site.siteMetadata.title} </title> <meta name="description" content={data.site.siteMetadata.description} /> </Helmet> ) } export default Seo 404.jsやabout.jsにもSeoをインポートしておく。 ページごとのメタデータの値を設定する [例] about.js の <Seo/>の要素に値を指定する。 const AboutPage = ({ data }) => ( <Layout> <Seo pagetitle="ESSENTIALSについて" pagedesc="食べ物についての情報を発信しているサイトです。" /> seo.jsではpropsを利用して各ページで設定したプロパティ値を取得する。 //ここにpropsを追記 const Seo = props => { const data = useStaticQuery(graphql` query { site { siteMetadata { title lang description } } } `) //三項演算子を利用 aaa ? bbb : ccc (aaaを評価し、trueならbbbをfalseならcccを返す) const title = props.pagetitle ? `${props.pagetitle} | ${data.site.siteMetadata.title}` : data.site.siteMetadata.title //論理演算子を使用  aaa || bbb (aaaを評価し、aaaがtrueならaaaを、falseならbbbを返す) const description = props.pagedesc || data.site.siteMetadata.description return( <Helmet> <html lang={data.site.siteMetadata.lang} /> <title>{title}</title> <meta name="description" content={description} />     </Helmet> ) } export default Seo ページのURLやOGP(Open Graph Protocol)を追加する //locationを追加 const AboutPage = ({ data, location }) => ( <Layout> <Seo //下記を追加 pagepath={location.pathname} //ページのURLを取得するlocationプロパティを利用 pageimg={data.about.childImageSharp.original.src} pageimgw={data.about.childImageSharp.original.width} pageimgh={data.about.childImageSharp.original.height} /> seo.jsにも定数とreturnで返すmetaを追加 //定数として追加するもの const url =props.pagepath ? `${data.site.siteMetadata.siteUrl}${props.pagepath}` : data.site.siteMetadata.siteUrl //今回はデフォルト用にでthumb.jpgを登録しているのでこのように記載 const imgUrl = props.pageimg ? `${data.site.siteMetadata.siteUrl}${props.pageimg}` : `${data.site.siteMetadata.siteUrl}/thumb.jpg` const imgw = props.pageimgw || 1280 const imgh = props.pageimgh || 640 //returnの中に追加するもの <link rel="canonical" href={url} /> <meta property="og:site_name" content={data.site.siteMetadata.title} /> <meta property="og:title" content={title} /> <meta property="og:description" content={description} /> <meta property="og:url" content={url} /> <meta property="og:type" content="website" /> <meta property="go:locale" content={data.site.siteMetadata.locale} /> <meta property="fb:app_id" content={data.site.siteMetadata.fbappid} /> <meta property="og:image" content={imgUrl} /> <meta property="og:image:width" content={imgw} /> <meta property="og:image:height" content={imgh} /> gatsby-config.jsのsiteMetaのsiteMetadataにも下記を追加し seo.jsのクエリにも値を追加する siteUrl: 'https://unruffled-austin-8f6fcf.netlify.app/', locale: `ja_JP`, fbappid:`XXXXXXXXXXXXXX`, PWA(Progressive Web Apps)対応用のパッケージを用意する マニフェストファイルを追加する。 npm install gatsby-plugin-manifest gatsby-config.jsには下記を追記 { resolve: `gatsby-plugin-manifest`, options: { name: `ESSENTIALS エッセンシャルズ`, //アプリ名を指定 short_name: `ESSENTIALS`, //アプリの短縮名 start_url: `/`, //アプリを開始するURL background_color: `#ffffff`, //アプリの起動画面の背景色を指定 theme_color: `#477294`, //テーマカラー、ブラウザやアプリのツールバーで使用される display: `standalone`, //アプリの表示モードを指定 icon: `src/images/icon.png` //アプリのアイコンを指定 }, }, オフラインに対応するファイルを追加する npm install gatsby-plugin-offline gatsby-config.jsには下記を追記 `gatsby-plugin-offline`, 完成したサイトをデプロイし、アイコンやアプリ化したときの動作を確認する。 SEOのスコアがアップし、PWAの可能になった表示へ変化しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React × TypeScript】ES6での基本的なコンポーネント作成

※reactとtypescriptともに初学者のためもっといい記法などあるかもしれません、何か知見があればご共有いただけると嬉しいです 執筆の経緯 Reactビギナーズガイドで勉強していたのですが、Vueなどは既に学習済みでして、少しステップアップして単に参考書を模写するのではなく、最新バージョンにしてtypescriptも導入するなんてやっていたらつまずくポイントも多かったので基本的なコンポーネント作成に絞って解説します。 ちなみにバージョンは "react": "^17.0.2" "typescript": "^4.3.5" コンポーネントの全体像 tsxでの記載になります。 表をレンダリングして、theadをクリックするとレコードを並び替えるコンポーネントです。 import React from 'react' const initialData: Array<[string, string, string, string, string]> = [ [ "The Lord of the Rings", "J. R. R. Tolkien", "English", "1954-1955", "150 million" ], [ "Le Petit Prince (The Little Prince)", "Antoine de Saint-Exuperty", "French", "1943", "140 million" ], [ "And Then There Were None", "Agatha Christie", "English", "1939", "107 million" ], [ "Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754-1791", "100 million" ], [ "The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million" ], [ "She: AHistory of Adventure", "H. Rider Haggard", "English", "1887", "100 million" ] ] type propTypes = { headers: [string, string, string, string, string] } type stateTypes = { data: Array<[string, string, string, string, string]> } export default class Excel<P = propTypes, S = stateTypes> extends React.Component<propTypes, stateTypes> { constructor(props: Readonly<propTypes>) { super(props) this.state = { data: initialData } this._sort = this._sort.bind(this) } _sort(e: any){ let column = e.target.cellIndex let data = this.state.data.slice() data = data.sort(function(a, b) { return a[column] > b[column] ? 1 : -1 }) this.setState({ data: data }) } render() { return ( <table> <thead onClick={this._sort}> <tr> { this.props.headers.map((header: string, idx: number) => { return ( <th key={idx}>{header}</th> ) }) } </tr> </thead> <tbody> { this.state.data.map((row: Array<string>, idx: number) => { return ( <tr key={idx}> { row.map((item: string, idx: number) => { return ( <td key={idx}>{item}</td> ) }) } </tr> ) }) } </tbody> </table> ) } } 呼び出し元はこう、 propsに値を入れ込むためにはどうするかわかりませんでしたが、呼び出し元で定義するんですね。 import React from 'react'; import '../css/App.css'; import Excel from "./component/Excel"; const headers: [string, string, string, string, string] = [ "タイトル", "著者", "言語", "出版年", "売上部数" ]; export default class App extends React.Component { render() { return ( <div className="App"> <Excel headers={headers} /> </div> ); } } React.Componentの継承 React.createClass() というメソッドを使ってコンポーネント作成すると参考書には記載されていましたが、 最新のバージョンではこのメソッドはなくなったようです。 代わりに、class定義を行いReact.Componentを継承します。 上ではTypeScriptに怒られるので細かく定義していましたが、基本形は以下になります。 export default class Excel extends React.Component { render(){ return ( ) } } stateの初期化 どこでやるんかぁと気になりましたが、constructor内でのみthis.stateの定義が許されているらしいです。 constructor(props: Readonly<propTypes>) { super(props) this.state = { data: initialData } this._sort = this._sort.bind(this) } thisのバインド クラス内でthisを利用するにはconstructor()でbindが必要。 これをしないとrender以外でthisはundefinedになります。 bindした変数はちゃんと同変数名に代入しないといけません。 constructor(props: Readonly<propTypes>) { super(props) this.state = { data: initialData } this._sort = this._sort.bind(this) } クラスへの型指定 typescriptでは型定義をしてあげないとクラス内で、 変数利用するときなどにエラーを吐かれてしまいます。 クラス内に直書きしてしまうと、汚くなるので、type宣言して当て込んであげるのがいいかと思います。 type propTypes = { headers: [string, string, string, string, string] } type stateTypes = { data: Array<[string, string, string, string, string]> } export default class Excel<P = propTypes, S = stateTypes> extends React.Component<propTypes, stateTypes> {} 感想 いやぁ難しいですね Vueができるとはいえ、Reactとなるとカッチリしててちょこちょこエラーにはまります。。。 コンポーネントの取り回しとかなんとなく雰囲気は似てるのでやってけそうですが疲れます。 ここまでお付き合いいただきありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを基本からまとめてみた【5】【Props(プロップス)】

はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、卒業後、フロントエンドのエンジニアを目指す事に。 Reactの学習した事を言語化し、認識の深化による備忘録として記載。 Propsとは 親コンポーネントから子コンポーネントへ値を渡す為の仕組み。 親 ⇨ 子の1方向のみ。子コンポーネントでデータの書き換えは不可。 Propsのメリット コードの記述量が減る 効率よくフロントエンド開発が行える Component + PropsはUI開発において定番 Button.jsの修正 Button.js import React from "react"; const Button = (props) => { return ( <button className="ui basic button"> <i className="icon user" /> {props.title} </button> ); }; export default Button; App.js import React from "react"; import Button from "./Button"; const App = () => { return ( <div> <div>App</div> <Button title="post" /> <Button title="edit" /> <Button title="delete" /> <Button title="submit" /> </div> ); }; export default App; 参考サイト 【React入門】#5 Props(プロップス)の理解
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Next.js】TypeScript, Tailwind CSS, Prettier, ESLint を導入したプロジェクトを開始(Webtorm)

はじめに Next.jsでTypeScript Tailwind CSS Prettier ESLintを導入しながらプロジェクトを開始する手順です。 自分はエディタにJETBRAINSのWebStormを使っているのでWebStormでの設定方法も記述してます。 Typescript が導入された Next.js プロジェクトを開始 yarn create next-app プロジェクト名 --typescript Tailwind CSS を導入 yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest yarn tailwindcss init -p tailwind.config.js postcss.config.js が作成される。 tailwind.config.js に JITモード と purge の設定 mode: "jit",を追加 purge: []の部分に設定を追加。 自分の場合はsrc配下でpagesやcomponentsディレクトリなどを管理したいため、下記記述を設定している。 プロジェクトのディレクトリ構成に合わせて設定する。 tailwind.config.js module.exports = { mode: "jit", purge: ['./src/**/*.{js,ts,jsx,tsx}'], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], } _app.tsx を修正 _app.tsxの import '../styles/globals.css'を import 'tailwindcss/tailwind.css'に置き換える _app.tsx import 'tailwindcss/tailwind.css' import type { AppProps } from 'next/app' function MyApp({ Component, pageProps }: AppProps) { return <Component {...pageProps} /> } export default MyApp Prettier を導入 yarn add --dev --exact prettier WebStormで設定 Apply → OK これでファイルsave時に自動でコードフォーマットをしてくれる。 Eslint を導入 yarn add eslint eslint-config-prettier --dev WebStormで設定 Apply → OK これでファイルsave時にリントが実行される。 参考 ありがとうございました! https://nextjs.org/docs/basic-features/typescript https://tailwindcss.com/docs/guides/nextjs https://qiita.com/sena-anny/items/a1ae73dc95e6f8af33ee https://qiita.com/shinshin86/items/c8b1f6b22b1bef3074c7 https://qiita.com/soarflat/items/06377f3b96964964a65d
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.memoとuseMemoとuseCallbackによるパフォーマンス改善

本記事の目的 React.memo useMemo useCallbackの基本的な使い方や具体的な利用用途を調査した内容を備忘録として記録する。 Reactを用いたシステムのパフォーマンス最適化の手段を知りたい useMemoとか聞いたことはあるけど、どこに使うべきかわからない これらのメソッドを使ったことがない 上記のような方々が参考になるような記事を目指します。 パフォーマンス最適化とは 一般的に不要な処理を削減したり、レンダリングとは関係のない処理を遅延させたりすることでUXを向上させることを目的に行われます。 Reactにおいては計算結果が前回の結果と等価となるような不要な再計算や不要なコンポーネントの再レンダリングを防ぐことがパフォーマンス最適化の第一歩となり得ます。 Reactではパフォーマンスを最適化する手段として下記のメソッドが用意されています。 - React.memo - useMemo - useCallback パフォーマンス最適化においては「とりあえずこれをやっておけばいいのね」という精神は捨てて、パフォーマンスの向上が見込めるかを判断しながら実施していく必要があります。 上記の関数も意味のない箇所で使うとパフォーマンスが向上しないどころか、逆にパフォーマンスが下がる結果に繋がる可能性もあります。 React.memo コンポーネントをメモ化するReactが提供するメソッドです。 コンポーネントをメモ化することでコンポーネントの不要な再レンダリングを防ぐことができます。 メモ化(英: Memoization)とは、プログラムの高速化のための最適化技法の一種であり、サブルーチン呼び出しの結果を後で再利用するために保持し、そのサブルーチン(関数)の呼び出し毎の再計算を防ぐ手法である。 引用元:Wikipedia メモ化とは計算やレンダリング結果を保持して、その結果を再利用する手法です。 キャッシュはデータを保持して再利用しますが、同じようなイメージでメモ化では計算結果やレンダリング結果を保持して再利用します。 React.memo によるパフォーマンス最適化 以下のようなコンポーネントの不要な再レンダリングを防ぐことで、パフォーマンスの向上を期待できます。 レンダリングまでの処理コストが高いコンポーネント 頻繁に再レンダリングされるコンポーネントの子コンポーネント 上記に該当しないようなコンポーネントに関してはReact.memoを利用するメリットが低い可能性があります。 React.memo の使い方 基本構文は以下の通りです。 React.memo(コンポーネント); 例えば、Greet(挨拶)コンポーネントをメモ化する場合は以下のように記述します。 type Props = { text: string; }; const Greet: React.FC<Props> = ({ text }) => { return <h1>{`Hello. ${text}`}</h1>; }; const memorizedGreet = React.memo(Greet); React.memoは引数の等価ではないと判定した場合にのみ再レンダリングを実行させます。反対に等価と判定した場合は再レンダリングを実行せずにメモ化したコンポーネントを再利用します。 上記のGreetコンポーネントでは、引数のtextが更新されるまでは再レンダリングが実行されません。 具体的な利用例 React.memo未使用 ※本記事のコンポーネントではコンソールに値が出力されている=再レンダリングが実行されている証明として説明します。 import { Button } from '@material-ui/core'; import { FC, memo, useState } from 'react'; type Props = { count: number; }; const Child: React.FC<Props> = ({ count }) => { console.log('render child'); return <p>{`Child: ${count}`}</p>; }; const Index: FC = () => { console.log('render parent'); const [parentCount, setParentCount] = useState(0); const [childCount, setChildCount] = useState(0); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo未使用</h1> <p>{`Parent: ${count1}`}</p> <Child count={count2} /> <Button variant="contained" color="default" onClick={() => setParentCount(parentCount + 1)}> Parent Count Up </Button> <Button variant="contained" color="primary" onClick={() => setChildCount(childCount + 1)}> Child Count Up </Button> </div> ); }; export default Index; この事例ではChildコンポーネント(以下、子コンポーネントと呼ぶ)のレンダリングには関係のないparentCountが更新された場合も子コンポーネントが再レンダリングされていることがわかります。 React.memo使用 import { Button } from '@material-ui/core'; import { FC, memo, useState } from 'react'; type Props = { count: number; }; const Child: React.FC<Props> = ({ count }) => { console.log('render child'); return <p>{`Child: ${count}`}</p>; }; // Childコンポーネントをメモ化 const MemorizedChild = memo(Child); const Index: FC = () => { console.log('render parent'); const [parentCount, setParentCount] = useState(0); const [childCount, setChildCount] = useState(0); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo使用</h1> <p>{`Parent: ${parentCount}`}</p> <MemorizedChild count={childCount} /> <Button variant="contained" color="default" onClick={() => setParentCount(parentCount + 1)}> Parent countup </Button> <Button variant="contained" color="primary" onClick={() => setChildCount(childCount + 1)}> Child countup </Button> </div> ); }; export default Index; この事例では子コンポーネントのレンダリングには関係のないparentCountが更新された場合は子コンポーネントの再レンダリングを防いでいます。 そして、子コンポーネントの引数であるchildCountが更新されると再レンダリングが実行されています。 効果的なメモ化 レンダリングの処理コストが高いコンポーネントのメモ化 import { Button } from '@material-ui/core'; import { FC, memo, useState } from 'react'; type Props = { count: number; }; const Child: React.FC<Props> = ({ count }) => { // 重い処理 let i = 0; while (i < 1000000000) i++; console.log('render child'); return <p>{`Child: ${count}`}</p>; }; const MemorizedChild = memo(Child); const Index: FC = () => { console.log('render parent'); const [parentCount, setParentCount] = useState(0); const [childCount, setChildCount] = useState(0); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo使用</h1> <p>{`Parent: ${parentCount}`}</p> <MemorizedChild count={childCount} /> <Button variant="contained" color="default" onClick={() => setParentCount(parentCount + 1)}> Parent countup </Button> <Button variant="contained" color="primary" onClick={() => setChildCount(childCount + 1)}> Child countup </Button> </div> ); }; export default Index; この事例では重い処理を持つ子コンポーネントをメモ化しているため、子コンポーネントとは関係のないparentCountが更新された時に重い処理を実行していません。 このように重い処理を持つコンポーネントのメモ化はパフォーマンス最適化に効果的である可能性が高いです。 更新頻度の高いコンポーネントの子コンポーネントのメモ化 import { FC, memo, useEffect, useState } from 'react'; const Child: React.FC = () => { console.log('render child'); return <p>Child</p>; }; const MemorizedChild = memo(Child); const Index: FC = () => { console.log('render parent'); const [count, setCount] = useState(0); useEffect(() => { const countUp = setTimeout(() => { setCount(count + 1); }, 100); return () => clearTimeout(countUp); }, [count]); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo使用</h1> <h1>{count}</h1> <MemorizedChild /> </div> ); }; export default Index; countが更新されるごとに親コンポーネントは再レンダリングされていますが、子コンポーネントはメモ化によって再レンダリングを防げています。 このように更新頻度の高い親コンポーネントの子コンポーネントのメモ化はパフォーマンス最適化に効果的である可能性が高いです。 意図しない再レンダリングを引き起こすメモ化 ここまでメモ化によって引数が更新されない限りは再レンダリングを防ぐことができている事例を紹介してきました。 ただし、コールバック関数を引数として子コンポーネントに渡す場合は必ず再レンダリングされます。 その理由はコールバック関数が再レンダリングの度に再生成されているからです。再生成されたコールバック関数は前回生成されたコールバック関数とは異なるオブジェクトなので等価ではないと判定されます import { Button } from '@material-ui/core'; import { FC, memo, useState } from 'react'; type Props = { countUp: () => void; }; const Child: React.FC<Props> = ({ countUp }) => { console.log('render child'); return ( <Button variant="contained" color="default" onClick={countUp}> Child Button </Button> ); }; const MemorizedChild = memo(Child); const Index: FC = () => { console.log('render parent'); const [count, setCount] = useState(0); const parentClick = () => { setCount(count + 1); }; // コンポーネントが再レンダリングされる度に新しいコールバック関数として生成される const childClick = () => { console.log('child click'); }; return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo使用</h1> <p>{`Count: ${count}`}</p> <Button variant="contained" color="primary" onClick={parentClick}> Parent Button </Button> <MemorizedChild countUp={childClick} /> </div> ); }; export default Index; このように子コンポーネントをメモ化しても、親コンポーネントが再レンダリングされる度に子コンポーネントも再レンダリングしてしまっています。 これは前述したように再レンダリングの度に子コンポーネントの引数であるコールバック関数が再生成されていることが原因です。 Reactではこの課題を解決する機能を提供しています。 それがコールバック関数をメモ化するuseCallbackというメソッドです。 useCallback 前述したようにコールバック関数をメモ化するReactが提供するメソッドです。 コールバック関数をメモ化することでコンポーネントの再レンダリングによって不要な再生成を防ぐことができます。 useCallback の使い方 基本構文は以下の通りです。 useCallback(コールバック関数, 依存配列); 例えば、与えた引数をコンソールに出力するGreet(挨拶)メソッドをメモ化する場合は以下のように記述します。 const Greet = useCallback((text: string) => { console.log({`Hello. ${text}`}) }, [text]); このように記述すると、Greet関数はメモ化されます。 コンポーネントが再レンダリングされてもuseCallbackの第二引数に指定されている値が更新されない限りはコールバック関数は更新されません。 また、第二引数はカンマ区切りでいくつでも指定することができます。 具体的な利用例 import { Button } from '@material-ui/core'; import { FC, memo, useCallback, useState } from 'react'; type Props = { countUp: () => void; }; const Child: React.FC<Props> = ({ countUp }) => { console.log('render child'); return ( <Button variant="contained" color="default" onClick={countUp}> Child Button </Button> ); }; const MemorizedChild = memo(Child); const Index: FC = () => { console.log('render parent'); const [count, setCount] = useState(0); const parentClick = () => { setCount(count + 1); }; const childClick = useCallback(() => { console.log('child click'); }, []); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>React.memo使用</h1> <p>{`Count: ${count}`}</p> <Button variant="contained" color="primary" onClick={parentClick}> Parent Button </Button> <MemorizedChild countUp={childClick} /> </div> ); }; export default Index; このようにuseCallbackでメモ化したコールバック関数を子コンポーネントに引数として渡すことで前項のような意図しない再レンダリングを防ぐことができます。 ただし、無闇にuseCallbackでコールバック関数をメモ化しても意味がありません。 例えば、useCallbackでメモ化したコールバック関数をメモ化していない子コンポーネントに渡したり、同一コンポーネント内で利用するのは効果的ではありません。 useMemo メモ化された値やレンダリング結果を返却するメソッドです。 コンポーネントの再レンダリング時に時間を要する計算を再実行させたくない場合などに有効です。 また、レンダリング結果をメモ化する場合、React.memoと同じようにコンポーネントの再レンダリングを防ぐことができます。関数コンポーネント内で関数コンポーネントをメモ化する場合はReact.memoでは意味がないので、useMemoを利用するようにしましょう。 useCallback の使い方 基本構文は以下の通りです。 useMemo(() => 計算ロジック, 依存配列); useMemo(関数コンポーネント,依存配列); 例えば、count変数を2倍した値をメモ化したい場合は以下のように記述します。 const result = useMemo(() => count * 2, [count]); このように記述すると、計算結果がメモ化されます。 コンポーネントが再レンダリングされても依存配列に指定したcountが更新されない限りは再計算されません。ただし、依存配列を指定しないと再計算されなくなるので注意が必要です。 また、第二引数はカンマ区切りでいくつでも指定することができます。 具体的な利用例 useMemo未使用 import { Button } from '@material-ui/core'; import { FC, useState } from 'react'; const Index: FC = () => { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const handleClick1 = () => { setCount1(count1 + 1); }; const handleClick2 = () => { setCount2(count2 + 1); }; const calculate = (count: number) => { let i = 0; while (i < 1000000000) i++; return count * 2; }; const calculatedCount2 = calculate(count2); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>Count1</h1> <p>{count1}</p> <Button variant="contained" color="primary" onClick={handleClick1}> Count1 + 1 </Button> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black', marginTop: '1rem' }}>Count2の2倍</h1> <p>{calculatedCount2}</p> <Button variant="contained" color="primary" onClick={handleClick2}> Count2 + 1 </Button> </div> ); }; export default Index; Count1はCount2の2倍の計算に関係ないにもかかわらず、Count1が更新された再レンダリング時にCount2の2倍の値が再計算されてパフォーマンスが低下しています。 このように計算ロジックが複雑(事例では単に重い処理)な計算結果をメモ化せずに利用すると、同一の結果が得られるとしても再レンダリング時に再計算されてしまいます。 useMemo使用 import { Button } from '@material-ui/core'; import { FC, useMemo, useState } from 'react'; const Index: FC = () => { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const handleClick1 = () => { setCount1(count1 + 1); }; const handleClick2 = () => { setCount2(count2 + 1); }; const calculate = (count: number) => { let i = 0; while (i < 1000000000) i++; return count * 2; }; const calculatedCount2 = useMemo(() => calculate(count2), [count2]); return ( <div style={{ margin: '1rem' }}> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black' }}>Count1</h1> <p>{count1}</p> <Button variant="contained" color="primary" onClick={handleClick1}> Count1 + 1 </Button> <h1 style={{ fontSize: '1.2rem', borderBottom: '1px solid black', marginTop: '1rem' }}>Count2の2倍</h1> <p>{calculatedCount2}</p> <Button variant="contained" color="primary" onClick={handleClick2}> Count2 + 1 </Button> </div> ); }; export default Index; このようにcount1の更新時の再レンダリングではcount2の計算結果は変更されないため、計算結果をメモ化することで不要な再計算を抑止できています。 処理が少ない計算や関数をメモ化すべきではない理由 これまで紹介してきたReact.memoやuseMemo、useCallbackはメソッドです。 非常に便利なメソッドですが、これらのメソッドも開発者が定義するメソッドと同じように処理を行っています。 このメソッドが処理する時間よりも処理時間が少ない処理の場合はパフォーマンスの低下に繋がる恐れがあります。 これらメソッドの処理に要する時間については言及できるほど分析できていませんが、それなりに重そうな処理に利用すれば大概はパフォーマンスの向上に繋がっています。 ざっくりと言うなら、容易な処理には使わないという意識を持って開発に着手すれば、特に問題にはならないでしょう。 最後に 普段何気なく使っているuseMemoやuseCallbackについて少しでも理解が深まりましたら幸いです。 ご指摘等あれば、ぜひコメント欄からお願いいたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む