20210306のReactに関する記事は15件です。

メモ化とは?かるーくまとめてみた!

概要

Reactを使ってよく耳にするのがメモ化。
正直今まで避けて通ってました、、。
しかし「今こそ!」しっかりと理解することでより良いアプリを作ることができると思うので、
学びつつ共有して行こうと思います〜!

この記事は、

  • メモ化ってよく聞くけどざっくりとしかわからない
  • メモ化ってどんな時に使うの?
  • 実際どんな動きをするの?

っていう方に有益な記事かもです!

メモ化とは?

まず、メモ化って言われても何かよくわかってなかったりしますよね。

そんな方に説明しますと、
「計算結果を保持することで、もう一度計算を行うときに変更がなければ再利用する」
というものです。

簡単に言えば、キャッシュ的なことです。

計算結果を保持していることで、
もう一度同じ結果を返す場合に
その都度計算し直すことがなくなるので
パフォーマンスが向上するよってことです。

また具体的には、
React.memouseCallbackuseMemoという手段があります。

メモ化は再度同様の計算をする必要がない分パフォーマンスが上がる反面、
これらの手段を使うためのコストの方が高いという場合もあり、
むしろ使った方が余計にメモリを食ってしまう。
なんてこともあるのでここでしっかりと理解して上手く使えるようになりましょう!

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を使うべきではないです。

これらはレンダリングの度に動きますし、最適化が必要なときは思った以上にないっぽいので見極めは大切だと思います。

終わりに

メモ化は、正直使わなくとも開発自体にはあまり影響がないかもです。

しかし、使うことでアプリの使いやすさ、パフォーマンスが向上するので適材適所、上手く分析して使うといいと思います。

まあ結局は使う必要性がでたらって感じで問題ないと思いますね!
なので日頃からパフォーマンスチューニングを心がけるといいのでは!

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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-06 20.58.50.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.ts
import { 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.tsx
import 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.tsx
import 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を動的に埋め込む方法でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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する。

です。

.htaccess
RewriteEngine 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の公式ページでした。
やはり、公式ページをちゃんと見るのが大事。

こちらにちゃんと
image.png

と書かれていました。。。。

公式、大事。

という事で、無事.htaccessを追加してbuildを行いこれでOKとルンルン気分でアップロードしアクセスしたんですが。。。

落とし穴がありました。

ならん。
Not Found変わらない。

なぜだ!!!

書き方が悪いのかと調べているとこちらの記事に辿りつきました。

この記事内の注釈に

※mod_rewriteが利用できることが前提です。

と書いてありました。

ん?

今までレンタルサーバしか利用した事なかったけど、.htaccessって標準で使えるものなの?
さくらVPSにApacheのインストールは自分でしたから.htaccessが使える設定じゃないんじゃないの?

と思い、バージョン、設定を確認したところ

Apacheのバージョン:2.4.6

設定で.htaccessが無効になっていたので、こちらの記事を参考に有効に変更しました。

これで、無事に遷移(ルーティング)ができました。
ぱちぱち

終わりに

実はこの解決にいたるまで、3日ほど検索を行っていました。
なかなか、解決に辿り着けずもやもやした毎日を過ごしていました。

ですので、同じ事で悩んでいる人がもしいた時の為に今回初めて記事を投稿しました。
誰かの約に立てば嬉しいですね。

今後はもっとQiitaなどのサービスを活用して理解を深めていきたいと思います。

今回ひとつ解決したけど、また壁にぶつかりそう。。。。
その時はまた記事にしたいと思います!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React(material-ui)カスタマイズ

Reactでチャットボット作ろう!
今回は以下のとらゼミさんの動画を参考にチャットボットを作成中。です!
現在はお問い合わせ内容をSlackに送るところまで出来ました。
非常にわかりやすく解説してくれるので、理解が深まります!

動画でもあるんですが、
今回は以下の内容を!

Material-ui

Material-uiのカスタマイズ方法です!
Hook APIを使用してやります。

インストール

npm install --save @material-ui/styles

import

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>
  )
}

感想

まだ作成途中ですが、
コンポーネントの設計がしっかり出来ているから、とてもコードが理解しやすいのだろうと
思うところです。私も何か作るときは自分なりに考えて設計を組み立てていきます!

今回は以上、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

週次報告 #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割くらい理解できたこと

つまづいて、今でもよくわかっていないこと

  • Reactで、まだthunkを絡めてstore作るのが難しい。どうしても半分コピペになる(それでいいのかも?)

次週何をやるべきか

  • いつものReact復習
  • 今週やったバックエンドの復習
  • 次のポートフォリオ制作の情報収集と実装計画(今のところFirebaseとReactでチャットアプリを作りたいと思っている)

内容は以上です。
みなさんコロナに負けず、頑張りましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

お猿さんのためのReactまとめ: Vol.2 ライフサイクル編

1...ライフサイクルとは?

  • コンポーネントの中の「時間の流れ」。
  • 人の一生に例えて、生まれて、成長して、死んでいく流れ(循環)。
  • それぞれの段階で必要な処理を記述する。
  • そのコンポーネントの外に影響を与える関数を記述するため。
    • DOM変更
    • API通信
    • ログ出力
    • setState() .....etc
  • 副作用=適切な場所に配置すべき処理。

React.png

2...実際のコードと画面で確認する

React (1).png
React (2).png

参考:日本一わかりやすいReact入門【基礎編】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ファイルにコンポーネントが散らばった

覚書です。

ディレクトリで整理するほどでもないけど、細かく仕分けたら一つのファイルに大量のコンポーネントが散乱してしまったときの対処法です。画面に表示されるコードが少なくなります。
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オブジェクトでコードの折り畳み機能を使うと一行にまとまってくれるので画面がスッキリします。
ランディングページ等で活躍します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでフッターをコンポーネント化する方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自作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の部分みたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.

01.png

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.

02.png

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:

03.png

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!

04.jpg

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.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-router-domでCannnot Getとなった時の対処法

背景

いつものようにreact-router-domを利用していると急にCannot Getとか言われて?になったので、備忘録として
残しておく。WhyHowを突き詰めることで知識と対応力をつけていきます。?
ちなみにwebpackを利用しているため、今回はそちらの解決方法を紹介するつもりです。
間違っている点や疑問点はビシビシコメントください?

Why

スクリーンショット 2021-03-06 1.11.38.png

そもそも何でこんな問題が発生しているのかについて考えいきましょう!
ブラウザは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.js
 output: {
    ....
    publicPath: '/'
 },
 devServer: {
    ...
    historyApiFallback: true,
  },

publicPathhistoryApiFallbackの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.

公式にあるようにCSSJavaScript等のasset群に対して、ベースURLを提供します。 下記のようにpublic Pathを指定するとpathが変更されていることがわかります。

webpack.config.js
 output: {
    ....
    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はデフォル設定だとfilenameindex.htmlで吐き出してしまうので、どちらかにfilenameを追加しましょう。

webpack.config.js
 plugins: [
    ....
    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問題は解決できます。

参考

【意訳】Webpackの混乱ポイント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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で見ると、怒られていることがわかります。

image.png

この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でも見てみましょう!エラーが出ていることがわかります。
image.png
このようにClassでも問題なく型が使えます!
Classで型安全にコードを書きたい方は是非ご利用ください!

おわりに

今回は、自作の型について紹介しました。
開発する上で自作の型を作ることは頻繁にあるので、作れるようにしておくと良いとも思います。

Next: 【TypeScriptハンズオン③】 ~webアプリとして動かそう!~

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactライフサイクル

image.png

Mount

 コンポーネントが配置される(生まれる)期間

Update

 コンポーネントが変更される(成長する)期間

Unmount

 コンポーネントが破棄(死ぬ)期間
image.png

Mounting

constructor()

初期化(stateなど)

render()

VDOMを描画(JSXをリターン)

componentDidMount()

render()後に一度だけ呼ばれる
リスナーの設定やAPI通信に使われる 

Update

render()

VDOMを再描画

componentDiaUpdate()

再render()後に呼ばれる
スクロールイベントや条件付イベント

Unmount

componentWillUnmount()

コンポーネントが破棄される直前
リソースを解放するため、リスナーの解除など

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む