- 投稿日:2021-03-06T21:12:58+09:00
メモ化とは?かるーくまとめてみた!
概要
Reactを使ってよく耳にするのがメモ化。
正直今まで避けて通ってました、、。
しかし「今こそ!」しっかりと理解することでより良いアプリを作ることができると思うので、
学びつつ共有して行こうと思います〜!この記事は、
- メモ化ってよく聞くけどざっくりとしかわからない
- メモ化ってどんな時に使うの?
- 実際どんな動きをするの?
っていう方に有益な記事かもです!
メモ化とは?
まず、メモ化って言われても何かよくわかってなかったりしますよね。
そんな方に説明しますと、
「計算結果を保持することで、もう一度計算を行うときに変更がなければ再利用する」
というものです。簡単に言えば、キャッシュ的なことです。
計算結果を保持していることで、
もう一度同じ結果を返す場合に
その都度計算し直すことがなくなるので
パフォーマンスが向上するよってことです。また具体的には、
React.memo
、useCallback
、useMemo
という手段があります。メモ化は再度同様の計算をする必要がない分パフォーマンスが上がる反面、
これらの手段を使うためのコストの方が高いという場合もあり、
むしろ使った方が余計にメモリを食ってしまう。
なんてこともあるのでここでしっかりと理解して上手く使えるようになりましょう!React.memo
React.memo
はどんなことができるのでしょうか。
主に、コンポーネントをメモ化し、余計な再レンダリングをスキップできるのです。つまり、
- レンダリングにコストがかかるコンポーネントに対して余計なレンダリングをしないようにする
- カウントアップに関するコンポーネントのように、 何度も頻繁にレンダリングがされるコンポーネントに対して使う
このような場合に使用するとパフォーマンスをあげることができます。
では、どのように使うのかという話をしていきます。
⬇︎例import React, { useState } from "react"; const Count2Component = React.memo(props => { return <p>count2: {props.count}</p>; }); export const App = () => { console.log("render App"); const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const countUp1 = () => setCount1(c => c + 1); const countUp2 = () => setCount2(c => c + 1); return ( <> <button onClick={countUp1}>count1</button> <button onClick={countUp2}>count2</button> <p>count1: {count1}</p> <Count2Component count={count2} /> </> ); }通常、count1ボタンを押すと、
Appコンポーネント自体が再レンダリングされるので、
stateの変化があった<p>count1: {count1}</p>
に加え、<Count2Component count={count2} />
も再レンダリングされるはずです。しかし、実際上記のコードを動かすと
<p>count1: {count1}</p>
しか再レンダリングされません。これがどういったことを意味するのかというと、
React.memo(コンポーネント)
の形で生成されたコンポーネントは、
「親の再レンダリングに関係なく、
そのコンポーネントに依存する値(ここではcount2)に変更があった時のみ再レンダリングが発生する」
ということになります。この場合、
count1ボタンが押され、Appコンポーネントが再レンダリングされてもcount2自体に変更がないので<Count2Component count={count2} />
は再レンダリングされないということになります。useCallback
次に、
useCallback
はどう使うのかという話をしていこうと思います。まず
useCallback
ができることは、
useCallback
はメモ化されたコールバックを返すことができるhookです。つまりはどういうことかというと、
例えば親コンポーネントから渡されるpropsが変化する場合に、通常はこのコンポーネントは全部再レンダリングされます。しかし、もしこのpropsが自身のコンポーネントにとって関係のないpropsである場合、メモ化されたコールバックを返すことでパフォーマンス向上が期待できるといったものです。
まあとりあえず例ですね。
⬇︎例import React, { useState, useCallback } from "react"; const Count2Component = React.memo(props => { return <p>count2: {props.count}</p>; }); export const App = () => { console.log("render App"); const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const countUp1 = () => setCount1(c => c + 1); const countUp2 = useCallback(() => setCount2(c => c + 1), [count2]); return ( <> <button onClick={countUp1}>count1</button> <button onClick={countUp2}>count2</button> <p>count1: {count1}</p> <Count2Component count={count2} /> </> ); }上記のコードでは、
count1ボタンが押され、Appコンポーネントが再レンダリングされてもcount2自体に変更がないので<Count2Component count={count2} />
は再レンダリングされません。これは、
useCallback
の
useCallback(コールバック関数, 依存配列)
という構文における依存配列(ここではcount2)の値に変化がないからです。そして上記の例にて一番重要なのは、
Count2Componentにて、React.memo
が使用されている点です。これがないと
useCallback
が上手く機能しないんですよね。なぜかというと、
useCallback
はcountUp2がメモリ内の、つまりメモ化された値と同じかどうかでCount2Componentを再レンダリングするか判断するものです。何が言いたいかというと、
React.memo
を使わず、親のコンポーネントが再レンダリングされたらuseCallback
があろうとなかろうと意味がないのです。つまりは
useCallback
は親コンポーネントが再レンダリングされないからこそ初めて意味を持つんですよね。ここは絶対注意が必要なので抑えておきましょう!
useMemo
最後に、
useMemo
について話そうと思います!
useMemo
とは、簡単にいうと値を計算するロジックなどで余計な計算をスキップしてパフォーマンスを向上させよう!といったものです。⬇︎例
import React { useState, useMemo } from "react"; export const App = () => { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const countUp = c => { let i = 0; while (i < 1000000000) i++; return c += 1; }; const count = useMemo(() => countUp(count2), [count]); return ( <> <p>Counter: {count1}</p> <button onClick={() => setCount1(count1 + 1)}>countUp</button> <p> Counter: {count2} | {countUp} </p> <button onClick={() => setCount2(count2 + 1)}>countUp</button> </> ); }通常、count1はcount1に+1するだけの処理だが、
useMemo
がない状態で実行するとcountUpが実行されて再レンダリングに時間がかかってしまいます。しかし上記のコードでは、countUpをメモ化することでcount1が再レンダリングされてもcountUpは実行されず、高速にコンポーネントを再レンダリングできます。
useMemo
はレンダリング結果もメモ化できるらしいのでコンポーネントの再レンダリングをスキップすることができるらしいです。
詳しくはこの参考記事を参照ください!(てかほとんどここを参照してますw)
https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2まとめ
これらははじめにいったようにどこで使うかが重要で、どこかしらにもポンポン使うべきではありません。
特に、関数宣言はコストの安い処理なので
useCallback
を使うべきではないです。これらはレンダリングの度に動きますし、最適化が必要なときは思った以上にないっぽいので見極めは大切だと思います。
終わりに
メモ化は、正直使わなくとも開発自体にはあまり影響がないかもです。
しかし、使うことでアプリの使いやすさ、パフォーマンスが向上するので適材適所、上手く分析して使うといいと思います。
まあ結局は使う必要性がでたらって感じで問題ないと思いますね!
なので日頃からパフォーマンスチューニングを心がけるといいのでは!参考資料
- 投稿日:2021-03-06T21:11:17+09:00
ReactでPaginationを実装する(material-ui)
はじめに
10件のアイテムを1ページあたり3件、計4ページに渡って表示するサンプルです。
実装
サンプルアイテム
const items = [ { id: '1', name: 'abe', from: 'nagoya' }, { id: '2', name: 'ito', from: 'iwate' }, { id: '3', name: 'uno', from: 'fukuoka' }, { id: '4', name: 'kato', from: 'tokyo' }, { id: '5', name: 'kurata', from: 'tottori' }, { id: '6', name: 'sato', from: 'kagoshima' }, { id: '7', name: 'takahashi', from: 'saitama' }, { id: '8', name: 'nishida', from: 'miyagi' }, { id: '9', name: 'hasegawa', from: 'gifu' }, { id: '10', name: 'yamada', from: 'aomori' } ]本体
import React, { useState, useEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { List, ListItem, ListItemText, ListItemAvatar, Avatar, Typography } from '@material-ui/core'; import Pagination from '@material-ui/lab/Pagination'; const useStyles = makeStyles((theme) => ({ list: { width: '100%', maxWidth: 360, backgroundColor: theme.palette.background.paper, }, pagenation: { '& > *': { marginTop: theme.spacing(2), display: 'inline-block', } }, })); const ListView = () => { const classes = useStyles(); const [page, setPage] = useState(1); //ページ番号 const [pageCount, setPageCount] = useState(); //ページ数 const [allItems, setAllItems] = useState([]); //全データ const [displayedItems, setDisplayedItems] = useState([]); //表示データ const displayNum = 3; //1ページあたりの項目数 const items = [...] useEffect(() => { setAllItems(items); //ページカウントの計算(今回は3項目/ページなので4ページ) setPageCount(Math.ceil(items.length/displayNum)); //表示データを抽出 setDisplayedItems(items.slice(((page - 1) * displayNum), page * displayNum)) }, []) const handleChange = (event, index) => { //ページ移動時にページ番号を更新 setPage(index); //ページ移動時に表示データを書き換える setDisplayedItems(allItems.slice(((index - 1) * displayNum), index * displayNum)) } return ( <> <List dense className={classes.list}> {displayedItems.map((item, index) => { const labelId = `label-${item.id}`; return ( <ListItem key={index} button> <ListItemAvatar> <Avatar alt={item.name} src="/static/images/avatar/xxx.jpg" /> </ListItemAvatar> <ListItemText id={labelId} primary={item.name} secondary={ <> <Typography component="span" variant="caption" display="block" color="textPrimary" > {item.from} </Typography> </> } /> </ListItem> ); })} </List> <div className={classes.pagenation} style={{textAlign:'center'}}> <Pagination count={pageCount} page={page} variant="outlined" color="primary" size="small" onChange={handleChange} /> </div> </> ) } export default ListView;実行結果
- 投稿日:2021-03-06T19:07:01+09:00
ReactでGoogle Analyticsを動的に埋め込む方法(gtag.js使用)
この記事でまとめる内容
Google AnalyticsのトラッキングID または 測定IDは、envファイル等で決まった値を定義しておくのが一般的かと思いますが、
バックエンドのAPIからトラッキングID または 測定IDを取得して動的な埋め込みが必要なケースがあったのでその実装方法についてまとめたいと思います。gtag.jsを使用する理由
まず超重要なトラッキングIDと測定IDの違いについて
- トラッキングID
- ユニバーサル アナリティクス プロパティのID
UA-
で始まる形式のID- 測定ID
- Google アナリティクス 4 プロパティのID
G-
で始まる形式のID参考
https://support.google.com/analytics/answer/9539598?hl=ja事の始まりはこの辺りをちゃんと理解せずにとりあえずreact-gaを使ったことでした...
「React Google Analytics」でググる
⬇︎
とりあえず出てきたreact-gaを使うことを決める
⬇︎
Google Analyticsでプロパティ作る ← プロパティの種類についてよくわかってないので、デフォルトでGoogle アナリティクス 4 プロパティになっていたけどこの時全く気にせずw
⬇︎
サンプルを見ながらお試しで実装してみる
⬇︎
あれ全くトラッキングできてない...?と頭を抱えていましたが、理由はこちらの記事を読んでわかりました。
なるほど。react-gaが内包しているanalytics.jsでは、Google アナリティクス 4 プロパティに対応していなかったのね。。
なので、今回はこちらの記事を参考にgtag.jsを使用してAPIから取得したトラッキングID または 測定IDの埋め込み方法を整理していきます。
さっそく本題
まずはこんな感じでGoogleAnalyticsに関する処理をまとめたファイルを作成します。
GoogleAnalyticsUtil.tsimport { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; // idで検索できるように埋め込むscript用の名前を定義 const SCRIPT1_NAME = 'gtag'; const SCRIPT2_NAME = 'gtagScript'; /** gtag.js読み込み用関数 */ export const initializeGA = (googleAnalyticsId: string): void => { // scriptが既にある場合は一度削除 document.getElementById(SCRIPT2_NAME)?.remove(); document.getElementById(SCRIPT1_NAME)?.remove(); // トラッキングID or 測定IDが空の場合は終了 if (googleAnalyticsId === '') return; // gtag.jsをheadタグに埋め込み const script1 = document.createElement('script'); script1.id = SCRIPT1_NAME; script1.src = `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`; script1.async = true; document.head.appendChild(script1); // 実行用scriptをheadタグに埋め込み const script2 = document.createElement('script'); script2.id = SCRIPT2_NAME; script2.text = `window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', '${googleAnalyticsId}');`; document.head.appendChild(script2); }; declare global { /* eslint-disable @typescript-eslint/no-unused-vars */ interface Window { gtag?: ( key: string, trackingId: string, // eslint-disable-next-line camelcase config: { page_path: string } ) => void; } } /** トラッキング用関数 */ export const useTracking = (googleAnalyticsId: string): void => { const { listen } = useHistory(); useEffect(() => { const unlisten = listen((location) => { if (!window.gtag) return; if (googleAnalyticsId === '') return; window.gtag('config', googleAnalyticsId, { page_path: location.pathname }); }); return unlisten; }, [googleAnalyticsId, listen]); };あとはこの関数を埋め込むだけです。
こんな感じでreact-router-domでページを切り替える画面があるとします。MainApp.tsximport React from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import TopContainer from './containers/TopContainer'; import HogeContainer from './containers/HogeContainer'; import FugaContainer from './containers/FugaContainer'; const MainApp: React.FC = () => { return ( <Router> <Switch> <Route exact path="/" component={TopContainer} /> <Route path="/hoge" component={HogeContainer} /> <Route path="/fuga" component={FugaContainer} /> </Switch> </Router> ); }; export default MainApp;こちらを以下のように修正します。
MainApp.tsximport React from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import TopContainer from './containers/TopContainer'; import HogeContainer from './containers/HogeContainer'; import FugaContainer from './containers/FugaContainer'; import { initializeGA, useTracking } from './utils/GoogleAnalyticsUtil'; // ⭐️これを追加 const MainApp: React.FC = ({ googleAnalyticsId: string }) => { // ⭐️これを追加 useEffect(() => { // GoogleAnalytics gtag.js読み込み initializeGA(googleAnalyticsId); }, [googleAnalyticsId]); // ⭐️これを追加 // GoogleAnalytics トラッキング useTracking(googleAnalyticsId); return ( <Router> <Switch> <Route exact path="/" component={TopContainer} /> <Route path="/hoge" component={HogeContainer} /> <Route path="/fuga" component={FugaContainer} /> </Switch> </Router> ); }; export default MainApp;
googleAnalyticsId
がAPIから取得したトラッキングID または 測定IDです(この取得処理の部分については今回は省略しています)
initializeGA
は、useEffectの第二引数にgoogleAnalyticsId
を指定しているので、値に変更があった時のみgtag.jsを書き換えてくれます。
あとはuseTracking
内の処理によってページ移動(切り替え)が行われる度にトラッキングが行われます。
以上、Google Analyticsを動的に埋め込む方法でした。
- 投稿日:2021-03-06T19:04:17+09:00
react-routerでルーティングしているSPAをApacheに公開する
はじめに
始めてReactを使ってSPAを作成し、Webサーバ(さくらVPS)で公開しようとした時に、ルーティングがうまくいかずてこずったので。備忘録も兼ねて書きます。
状況
React Reduxを使用してSPAを作成していた。
・connected-react-router
・react-router
を使いルーティングを実現していた。ローカル環境では問題なくルーティングは動いていた。
問題点
「npm run build」してできたbuildフォルダ内のファイルを、Webサーバにアップロードしてアクセスしたら、ルーティングで読み込んでいたコンポーネントが表示されなかった。
「~/login」というパスをURLに打ち込むと「404:Not Found」が表示された。
疑った事
build時の設定(package.json)の設定を変更してbuildをして、特別な形のファイル書き出しを行わないといけないのではないか。
解決方法
結論を先に書くと
publicフォルダに.htaccessを追加してbuildする。
です。
.htaccessRewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.html [QSA,L]ここからは紆余曲折を書いていこうと思うので、急いでいる方はどうぞ読み飛ばしてください。
なかなかこの結論に達するまでに時間を費やしたので、変なテンションで書いていきたいと思います。まずは、connected-react-routerやreact-routerを使用したルーティングのbuild方法やデプロイ方法を調べようと思い、「react connected-react-router build デプロイ」なんて感じで検索し、いろんな記事を漁りました。
なかなか解決になるような記事は見つからず、途方にくれ始めてこのQiitaアカウントを作成し、質問を投稿してみました。(※この事が今回の中で一番の収穫であろう)
まぁ、そんな早く返信もあるはずもなく、Qiita内で検索できる事を知り(どんだけ使った事ないんだという、、、) Reactやreact-router関連の過去記事を漁りました。build・デプロイ関連の記事も沢山あるんですが、
いろんな記事を読んでいくうちに、自分の大きな勘違いに気づきました。
SPAなんだから、buildされてできるのはindex.htmlだけ
てっきりreact-routerを使うとそういうディレクトリ構成でbuildされると思っていたわけです。
なので、packege.jsonになにか設定を書くじゃないか、webpack.config.jsを修正するんじゃないかと違う方向ばかりに目が向いていました。そして、行きついた先が、create-react-appの公式ページでした。
やはり、公式ページをちゃんと見るのが大事。と書かれていました。。。。
公式、大事。
という事で、無事.htaccessを追加してbuildを行いこれでOKとルンルン気分でアップロードしアクセスしたんですが。。。
落とし穴がありました。
ならん。
Not Found変わらない。なぜだ!!!
書き方が悪いのかと調べているとこちらの記事に辿りつきました。
この記事内の注釈に
※mod_rewriteが利用できることが前提です。
と書いてありました。
ん?
今までレンタルサーバしか利用した事なかったけど、.htaccessって標準で使えるものなの?
さくらVPSにApacheのインストールは自分でしたから.htaccessが使える設定じゃないんじゃないの?と思い、バージョン、設定を確認したところ
Apacheのバージョン:2.4.6
設定で.htaccessが無効になっていたので、こちらの記事を参考に有効に変更しました。
これで、無事に遷移(ルーティング)ができました。
ぱちぱち終わりに
実はこの解決にいたるまで、3日ほど検索を行っていました。
なかなか、解決に辿り着けずもやもやした毎日を過ごしていました。ですので、同じ事で悩んでいる人がもしいた時の為に今回初めて記事を投稿しました。
誰かの約に立てば嬉しいですね。今後はもっとQiitaなどのサービスを活用して理解を深めていきたいと思います。
今回ひとつ解決したけど、また壁にぶつかりそう。。。。
その時はまた記事にしたいと思います!
- 投稿日:2021-03-06T18:58:51+09:00
React(material-ui)カスタマイズ
Reactでチャットボット作ろう!
今回は以下のとらゼミさんの動画を参考にチャットボットを作成中。です!
現在はお問い合わせ内容をSlackに送るところまで出来ました。
非常にわかりやすく解説してくれるので、理解が深まります!
動画でもあるんですが、
今回は以下の内容を!Material-ui
Material-uiのカスタマイズ方法です!
Hook APIを使用してやります。インストール
npm install --save @material-ui/stylesimport
import {createStyles, makeStyles} from '@material-ui/core/styles'¥¥¥¥¥関数定義
const useStyles = makeStyles(() => ( createStyles({ "button": { borderColor: '#FFB549', color: '#FFB549', fontWeight: 600, marginBottom: '8px', "&:hover": { backgroundColor: '#FFB549', color: '#fff' } } }) ));ここまで出来たら、以下のように。
useStylesをclassesに入れて、このように⇨className={classes.button}const Answer = (props) => { const classes = useStyles(); return ( <Button className={classes.button} variant="outlined" onClick= {() => props.select(props.content,props.nextId)} > {props.content} </Button> ) }感想
まだ作成途中ですが、
コンポーネントの設計がしっかり出来ているから、とてもコードが理解しやすいのだろうと
思うところです。私も何か作るときは自分なりに考えて設計を組み立てていきます!今回は以上、ありがとうございました。
- 投稿日:2021-03-06T18:54:17+09:00
週次報告 #4
お疲れ様です。
この記事は、すごく個人的な学習週次報告です。
自分自身の「目標の明確化」「学んだこと・わからないことの整理」「成長記録」のために書いています。今回は、”今週で主にやったこと”から、
”今週で学んだこと”、”つまづいたけど7、8割くらい理解できたこと”、
”つまづいて、今でもよくわかっていないこと”を整理して、
”次週何をやるべきか”を書いていきます。今週で主にやったこと
- React、ReacrRauter、Redux、ReactRedux、ReduxThunkの復習
- React:簡易的な掲示板の作成
- ReacrRauter:APIを使ったクイズアプリの作成
- Redux:TODOを管理できるツールの作成
- ReactRedux:Reduxで作ったツールを元にTODOアプリの作成
- ReduxThunk:ReacrRauterで作ったクイズアプリのデータをStore管理できるようにする
- バックエンドの基礎の勉強
- Express
- nodemonインストール
- express.routerでルーティング処理
- ejsを使ってview engineを設定
- Postmanを使って、自作したAPIにHTTPメソッドを投げてみた
- ExpressでクイズAPIと連携したクイズアプリを実装
- Git flowを一応意識してtestとPostmanで挙動確認しながら、Expressで掲示板APIを実装
- Firebase
- Hostingを試してみた
- Cloud Firestoreを触ってみた
- Cloud FunctionsとExpressで、Firestoreと連携したREST APIを実装&デプロイ
- Authenticationでメール認証・ログイン・ログアウト機能を実装
今週で学んだこと
”MVC”について学んだ
- アプリ設計を整理するための概念(規模拡大につれて破綻しないように)
- [それぞれの役割]
- Model
- DBとデータの取得・更新・削除・新規作成等 やり取り
- 取得データを扱いやすい形式に変換
- View
- HTMLを取得データと絡めて生成・表示
- Controller
- クライアントからのリクエストURLに応じて、あらかじめ設定した処理を行う
- リクエストURLと処理を紐づけることをルーティングと呼ぶ
- 動的なページ生成の際は次のような処理を行う
- クライアントからリクエストを受取
- Model経由でDBからデータを取得
- そのデータをViewに渡す
- 動的生成されたHTMLをクライアントに返す(=レスポンス)
- クライアント、M、Vの仲介役であり司令塔
”HTTPメソッド”について学んだ
- 基本の四つ
- GET(取得)
- POST(追加)
- PUT(更新)
- DELETE(削除)
ステータスコードについて学んだ
- とは…HTTP通信を行った結果を表す三桁の数字のこと
- 大きく分けると以下の5種類
- 1xx:情報
- 2xx:成功 ←頻出!
- 3xx:リダイレクション
- 4xx:クライアントエラー ←頻出!
- 5xx:サーバーエラー ←頻出!
”API”について学んだ
- Application Programming Interface
- インターフェイス:IT用語で「何か」と「何か」を繋ぐもの、という意
- 他で転用可能にした便利機能のこと(部品のような扱い)
- あらゆるAPI(ログイン機能、決算機能など)が、あらゆる企業(Google、LINEなど)から無料外部公開されている
- [メリット]
- 開発の効率化
- セキュリティの向上&ユーザー操作の利便化 * 例「(Googleなどの会員登録システムとの連携を導入した時)新たに会員登録しなくて済むし、安心!」
- 最新情報を簡単に取得可能
- WebAPIはさらに汎用的になったもの。HTTPリクエストをやり取りのベースにすることで、プログラミング多言語間での連携を可能にする
つまづいたけど7、8割くらい理解できたこと
- Firebase CLIのインストール時に、npmで’permission denied’エラーに
- これも以前引っかかった、sudoの権限設定の問題だった
- 解決記事:https://qiita.com/okohs/items/ced3c3de30af1035242d
つまづいて、今でもよくわかっていないこと
- Reactで、まだthunkを絡めてstore作るのが難しい。どうしても半分コピペになる(それでいいのかも?)
次週何をやるべきか
- いつものReact復習
- 今週やったバックエンドの復習
- 次のポートフォリオ制作の情報収集と実装計画(今のところFirebaseとReactでチャットアプリを作りたいと思っている)
内容は以上です。
みなさんコロナに負けず、頑張りましょう。
- 投稿日:2021-03-06T17:27:18+09:00
お猿さんのためのReactまとめ: Vol.2 ライフサイクル編
- 投稿日:2021-03-06T16:05:31+09:00
ファイルにコンポーネントが散らばった
覚書です。
ディレクトリで整理するほどでもないけど、細かく仕分けたら一つのファイルに大量のコンポーネントが散乱してしまったときの対処法です。画面に表示されるコードが少なくなります。
IDEやテキストエディタなどに付属しているコード領域の展開/折り畳み
の機能がないとあまり効果を発揮できません。import React from 'react' import styled from '@emotion/styled' const Hoge = { Wrapper: <div>...</div>, P: styled.p` ...; `, } const HogePage: React.VFC = () => ( <Hoge.Wrapper> <Hoge.P>hoge</Hoge.P>\ </Hoge.Wrapper> )これでHogeオブジェクトでコードの折り畳み機能を使うと一行にまとまってくれるので画面がスッキリします。
ランディングページ等で活躍します。
- 投稿日:2021-03-06T14:23:37+09:00
React初心者へ ReactとNode.js?フロントなのにサーバーサイド?
React初心者へ
Reactの復習をし始めたので、私も初心者ですが、React初心者向けに記事を書いてみました。
Reactの学習を始めてしばらくするとNode.jsに出会うと思いますが、フロントエンド開発なのになぜサーバーサイドの話が出てくるのかと疑問を頂いていませんか?
私もはじめはそう思っていました。Node.jsについてググって色々調べると「サーバーサイドのJavaSctipt」という間違った認識を得てしまいました。Node.jsはJavaScriptの実行環境のこと
「サーバーサイドのJavaSctipt」という認識のとおり、サーバーサイドで動く、少し書き方の違うJavaScriptなのかと思っていませんか?私はそうでした。
JavaScriptは通常ブラウザでしか動かないのですが、PCでも動くようにするためのソフトウェアがNode.jsなのです。詳しくはこちらの記事がおすすめです。
ReactはNode.jsなしでも動きます
htmlのscriptタグで
https://unpkg.com/react@17.0.1/umd/react.development.js
https://unpkg.com/react-dom@17.0.1/umd/react-dom.development.js
を読み込み、ReactDOM,renderメソッドを使用すれば動きます。なぜReact開発にNode.jsなのか
Reactでの開発時にサーバーサイドのJavaScript環境として知られるNode.jsを使う理由は、
①アプリケーション開発時のパッケージ管理の手間を省くため
②webpackやbabelなどの便利ツールを使用するため。などがあります。
①アプリケーション開発時のパッケージ管理の手間を省くため
Reactでアプリケーション開発を行っていると、様々なパッケージを使う必要があるが、それらのパッケージ同士は相互に依存関係にあります。
例えば、「パッケージAはパッケージBのver X.X.X、パーッケージCのver X.X.Xがないと正常に動作しない。」というような状況が各パッケージ毎にあります。アプリケーションの開発者はこれらのパッケージのバージョンを適切に管理する必要があるのですが、ある程度のアプリになるとそのパッケージを手動で管理するのは非常に困難です。Node.js環境で開発すると、パッケージを管理するためのnpmやyarnというツールを使用することでき、CLIからコマンドによりパッケージのインストールや依存関係の調整などが簡単に行うことができるのです。
②webpackやbabelなどの便利ツールを使用するため。
JavaScriptファイル、CSSファイルなどをバンドルするwebpack、最新のJavaScriptで書かれたコードを古いブラウザでも認識できる形にコンパイルするbabelなど、フロントエンド開発に欠かせないツールもNode.js環境があるために使用できています。CreateReactAppを利用しReact開発を始めた初心者は、あまり意識しない部分かもしれませんが、これもNode.jsの恩恵なのです。
終わりに
React界隈では名著の呼び声高い「りあクト!」を参考に勉強しました。作中で業務未経験には少しハードルが高いというニュアンスで書かれており、たしかにそうでしたが、おすすめです!
https://oukayuka.booth.pm/
- 投稿日:2021-03-06T10:35:35+09:00
自作apiでauthが効かない(laravel)
バックエンドにlaravel、フロントにreactを利用してSPAでwebアプリを作っているのですが、
authの処理ではまりました。
具体的には、フロント側からlaravelで自作したapiを利用する際、Authファサードを利用しても値がとれないという状態でした。解決方法
laravelで自作したapiのrouteはapi.phpに記述していたのですが、どうやらデフォルトではapi.phpはsession機能を読み込まないようです。
RouteServiceProvider.phpで以下の修正を行いました。
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::prefix('api') // ->middleware('api') ← 元々 ->middleware('web') ← 修正後 ->namespace($this->namespace) ->group(base_path('routes/api.php')); Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); }); }ちなみに、middleware('web')などで読み込んでいるいるのは、Kernel.phpの$middlewareGroupsの部分みたいです。
- 投稿日:2021-03-06T09:17:46+09:00
5 Simple Tricks to Boost Your Website Performance
Hi, Jerfareza here.
This time I would like to share a collection of tricks that I'm implementing in my websites to improve their performance.1. Use Smaller Font File
If you self-host your fonts, try to use modern file extensions woff or woff2 format instead of ttf. ttf (True Type Font) fonts are considered the 'old' type, developed as far as the 1980s. They typically have larger sizes than their counterparts woff (Web Open Font Format), a rather new type developed as recently as 2009 woff.
The size difference is evident here.
There are numerous online converters out there to help you convert ttf to woff/woff2 easily.
2. Avoid Adding Unnecessary Package
Every
npm
package that you add to your bundle is a cost. That's the undeniable truth. Do you really need that package that you may replace with a few lines of code or that package that you only use once on your page?One of the popular utility packages for time and date is moment.js. It's handy, yes, but if not used correctly, it may bloat the size of your client bundle. I stopped using it a while ago since I don't really show many dates on my website.
Always think before adding a new package to your code. The default is not to use. But if you have to add, use Bundlephobia to find out how big is the package you want, or if it has a better, slimmer alternative.
3. Defer Not Critical Third-Party Scripts
The easiest example of this is Google Analytics, which is an indispensable tool for all website owners. But it's also blocking the main thread and make Lighthouse complains every single time.
To deal with this, try to load the scripts last in your code. Typically you can put it in the last position before the closing tag of HTML.
In React I do something like:
{/* Global Site Tag (gtag.js) - Google Analytics */} <script defer src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`} /> </Html>This will result in the script being called last:
4. Utilize Code Splitting
Code splitting is the splitting of code into various bundles or components, which can then be loaded on-demand or in parallel. (from Mozilla)
Bundlers like Webpack or Rollup make it easy for us to separate some parts of our website page to load independently beside the main thread. With React it's even easier: dynamic-import.
import("./expensiveComponent").then(component => { <>YOUR HEAVY DUTY COMPONENT HERE</> });Even much easier in Next.Js:
import dynamic from 'next/dynamic'; const DynamicProfile = dynamic(() => import('components/Profile')); ... return ( <> <DynamicProfile /> </>5. Pay Attention to Above & Below the Fold
Ok, this last trick might not be a simple one. This may involve changing your website design and may apply more to mobile users compared to desktop users. But since people use smartphones most of the time, optimizing for mobile is more paramount than desktop.
Let's think of it this way. In mobile, you have minimal space, although screen size depends on the device type. And to make use of that precious space, your browser does not load everything all at once. It loads your website part-by-part.
The part of your website shown on your screen right after page load is called
'Above the fold'
. Everything else is not shown immediately, such as the footer or the main content below a beautiful hero image, is called'Below the fold'
. To boost the performance, all you have to do is optimize those visible parts. Simple. Anything else you can load later, it's not important to show in the initial.In my photography website, I purposely made a huge hero section covering the whole screen. Aside from a marketing point of view, I did this to improve the performance. All Google sees for the first time on my home page is that hero, and thus I need to maximize the optimization of that hero.
This gets me a score of 90 for mobile in Page Speed Insight!
Closing
That's it. I hope these tricks are useful.
One trick that I initially wanted to include in the article is optimizing images. But then again, image optimization is a huge and complex topic and not really a 'trick'. Plus, there are already so many great articles out there that delve deep into the matter.
- 投稿日:2021-03-06T02:41:25+09:00
react-router-domでCannnot Getとなった時の対処法
背景
いつものように
react-router-dom
を利用していると急にCannot Get
とか言われて?になったので、備忘録として
残しておく。Why
とHow
を突き詰めることで知識と対応力をつけていきます。?
ちなみにwebpack
を利用しているため、今回はそちらの解決方法を紹介するつもりです。
間違っている点や疑問点はビシビシコメントください?Why
そもそも何でこんな問題が発生しているのかについて考えいきましょう!
ブラウザはhttps://sample.com/
にアクセスするとサーバーサイドに問い合わせが飛びます。
サーバーは/(index.html)
を求めるものだと判断してindex.html
をレスポンスします。
そのため、もしhttps://sample.com/hello.html
にアクセスして、hello.html
が存在しない、かつ特にNot Found
処理をしていない場合はCannot GET
になってしまいます。今回の場合、
/sample
にアクセスが入り、サーバーはsample
ファイルが見つからずこのような結果になってしまってます。How
ここからは解決策を見ていきましょう!!
webpack
を利用している場合dist
等のプロジェクトフォルダーがビルドを行うことで、吐き出されていると思われます。
全てのURL
に対してhtmlファイルを吐き出すのはよくないでしょう。(SPAじゃない)
それではどのように解決するかというとwebpack.config
に次のような設定を追加しましょう!!webpack.config.jsoutput: { .... publicPath: '/' }, devServer: { ... historyApiFallback: true, },
publicPath
とhistoryApiFallback
の2行を追加しただけで動くようになりました。
これら2つの設定がどのような動きをするのか見てみましょう。publicPath
公式には次のようにあります。
The publicPath configuration option can be quite useful in a variety of scenarios. It allows you to specify the base path for all the assets within your application.
公式にあるように
CSS
やJavaScript
等のasset群に対して、ベースURLを提供します。 下記のようにpublic Path
を指定するとpathが変更されていることがわかります。webpack.config.jsoutput: { .... publicPath: 'https://asset.com/' },dist/index.html<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Hello World</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link href="https://asset.com/styles/index.css" rel="stylesheet"></head> <body> <div id="root"></div> <script defer src="https://asset.com/scripts/bundle.js"></script></body> </html>
url-loader
等に噛ませることも可能で、本番環境で画像等をCDNを利用して配信する場合、環境変数を通してローカルと本番環境でファイルpathの変更できます。
実は今回の問題に対してpublicPath
の設定が必要な場合と、そうでない場合の2種類があります。
ほとんどの場合publicPath
を指定しないで後述するhistoryApiFallback
を設定してあげることで動くのですが、プロジェクトのフォルダ構成やそれこそCDNの関係で必要となる場合があるので適宜設定してあげましょう。historyApiFallback
When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true:
そのままですね。これを
true
にしてあげることで、ファイルが見つからなった場合にindex.html
を返すようになります。
試しにプロジェクトファイルに2つのHTMLファイルを用意します。 この時の注意点ですが、HtmlWebpackPlugin
はデフォル設定だとfilename
をindex.html
で吐き出してしまうので、どちらかにfilename
を追加しましょう。webpack.config.jsplugins: [ .... new HtmlWebpackPlugin({ template: path.resolve(SRC_PATH, "./index.html"), inject: "body", }), new HtmlWebpackPlugin({ template: path.resolve(SRC_PATH, "./sample.html"), filename: "sample.html", }), ],これで
/sample.html
の場合はファイルが存在するので、https://hoge.com/sample.html
の場合はsample.html
がレスポンスされます。それ以外の場合はindex.html
がレスポンスされます。
historyApiFallback
を利用することでCannnot GET
問題は解決できます。参考
- 投稿日:2021-03-06T01:08:23+09:00
【TypeScriptハンズオン②】男もすなるTypeScriptといふものを、女もしてみむとてするなり ~自分だけの型を作ろう!~
この記はなに
TypeScriptを実際に触りながらTypeScriptを説く記の第2弾です。
TypeScriptが しょしんなる 方や、TypeScriptを いとをかし と思っているひとが対象です。この記で、こころよしと思った方は、LGTM✨を何卒よしなに願い申し上げ候。
過去と未来?
1. 【TypeScriptハンズオン①】男もすなるTypeScriptといふものを、女もしてみむとてするなり
2. 【TypeScriptハンズオン②】男もすなるTypeScriptといふものを、女もしてみむとてするなり ~自分だけの型を作ろう!~←この記
3. 【TypeScriptハンズオン③】 ~webアプリとして動かそう!~@ TypeScriptをインストールしていないひとはこちら?
【画像で説明】シンプルにTypeScriptを導入して使う方法
@ TypeScriptをいとをかしと思ったひとはこちら?
JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事ハンズオン
✅ これ以降古語は出てきません。ご安心ください
✅ VSCodeを使います型の作成(Interface)
TypeScriptは、変数や関数に型を明記することで、開発者がハッピー?になります。
今回は自作の型を作って?、ハッピー?になっていきます。自分だけの型を作るには、
interface
を使います。
自分だけの型とはどういうものなのでしょうか?例として関数の引数に、以下のようなオブジェクトを受け取りたいとします。
const kinotsurayuki = { bookName: "土佐日記", author: "紀貫之", year: 866, }このオブジェクトは、
bookName
がstring型,author
がstring型,year
がnumber型であることがわかります。
これを明示的に型を付けたい場合は、interface
を使ってBook
型を作ります。interface Book { bookName: string; author: string; year: number; }
interface
は以下のように、作られています。interface 型名 { 名: <型>; 名: <型>; 名: <型>; }この
Book
型は以下のように使います。型が一致しているので、TypeScriptのエラーはありません。const kinotsurayuki: Book = { bookName: "土佐日記", author: "紀貫之", year: 866, }一方で、
Book
型の構造に違反すると、以下のように怒られます。
この場合は、author
ではなく、headOffice
と記述してしまいました。const kinokuniya: Book = { bookName: "紀伊國屋書店", headOffice: "新宿", // タイプ '{bookName: string; headOffice: string; year: number; } 'はタイプ' Book 'に割り当てることはできません。 year: 1927 }実際にVSCodeで見ると、怒られていることがわかります。
この
Book
型は引数にも利用できます。const bookDetails = (details: Book) => { console.log(`${details.bookName}の著者は${details.author}で、生まれは${details.year}です。`); }予め型を定義しておくことで、使い回しが出来ます。
そのため、頻繁に使う型については用意しておくといいかもしれませんね!クラスの型の作成
クラスでも型定義を利用することが出来ます。
class Book { bookInfo: string; constructor(public bookName: string, public author: string) { this.bookInfo = `${bookName}の作者は${author}です。`; } }このように予め、型を宣言しておくことで、型の違うものが入った時に気づくことが出来ます。
問題ない例は以下です。const introduce = new Book("土佐日記", "紀貫之"); console.log(introduce.bookInfo); // 土佐日記の作者は紀貫之です。以下は、違反しているので、エラーが出てしまいます。
const bookPreview = new Book(897, "紀貫之"); // タイプ 'number'の引数は、タイプ 'string'のパラメーターに割り当てることができません。VSCodeでも見てみましょう!エラーが出ていることがわかります。
このようにClassでも問題なく型が使えます!
Classで型安全にコードを書きたい方は是非ご利用ください!おわりに
今回は、自作の型について紹介しました。
開発する上で自作の型を作ることは頻繁にあるので、作れるようにしておくと良いとも思います。Next: 【TypeScriptハンズオン③】 ~webアプリとして動かそう!~
参考文献
- 投稿日:2021-03-06T00:19:27+09:00
Reactライフサイクル
Mount
コンポーネントが配置される(生まれる)期間
Update
コンポーネントが変更される(成長する)期間
Unmount
Mounting
constructor()
初期化(stateなど)
render()
VDOMを描画(JSXをリターン)
componentDidMount()
render()後に一度だけ呼ばれる
リスナーの設定やAPI通信に使われるUpdate
render()
VDOMを再描画
componentDiaUpdate()
再render()後に呼ばれる
スクロールイベントや条件付イベントUnmount
componentWillUnmount()
コンポーネントが破棄される直前
リソースを解放するため、リスナーの解除など