20201126のReactに関する記事は9件です。

2020年React.js 最速マスターコース

Reactに難しいイメージが多いのか使える人を探しづらいという課題を感じています。

実際 Reactは長い歴史のなかで記述方法の変化、重要な機能が追加されつづけてきました。
そのすすんだ要素もふくめて、どうやって勉強するのがよいか、書いてみました。

基本的には公式のチュートリアルをうまく組み合わせて勉強すればよいと思います。

step.0 環境準備

何はともあれ、node をインストールする必要があります。
node.js は頻発に バージョンアップがはいるため、nvm で管理しましょう。

nvm

https://github.com/nvm-sh/nvm#installing-and-updating

nvm for windows の導入方法

https://docs.microsoft.com/ja-jp/windows/nodejs/setup-on-windows

Create React Appで勉強環境を構築

勉強をするためのアプリケーションを作れるようになりましょう。

Create React App は手軽に開発環境をつくれると同時に、
React開発のベストプラクティスがたくさんつめこまれています。

https://ja.reactjs.org/docs/create-a-new-react-app.html

Typescript はこちら
https://create-react-app.dev/docs/adding-typescript/

VSCodeの導入

https://code.visualstudio.com/

Step.1 React Tutorial の実施

まずは React の基本である State, Props, Event を下記のチュートリアルで体験しましょう。
https://ja.reactjs.org/tutorial/tutorial.html

わからないところは公式ドキュメントやGoogleで調べながらすすめます。

Step.2 React Hooks

次に現代のスタンダードになりつつある React Hooksを学びましょう。

React Hooks
https://ja.reactjs.org/docs/hooks-overview.html

概要を掴んだら、次は Step.1 のコードを React Hooks に書き直してみましょう。
その過程でわからなくったら公式ドキュメントを読みましょう。
終わる頃には半分ぐらいは理解できるようになっていると思います。

Step.3

次は TypeScript です。
使うか使わないかはわかれると思いますが、スタンダードになりつつあります。

TypeScript がはじめての人は下記をみてどうゆうものか把握します。5分で読めます。
英語なのでグーグル翻訳にかけると読みやすいです。

https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html

大枠掴んだら、React でどう使うのかを下記で把握します。

Typescript の書き方はバージョンアップやユーザのアイディアにより日々進化します。
react-typescript-cheatsheetにそのグッドプラクティスが集まっています。
少し難しくてもこのサイトの内容で覚えtmoraeru

React Typescript
https://react-typescript-cheatsheet.netlify.app/docs/basic/setup

Step.2 で作成した Hooks のアプリを TypeScript に書き換える

わからないところは react-typescript-cheatsheet を参考にしながらすすめます。

Step.4 Material UI

Material UI を学習します。
Material UI のサイトにはExampleが豊富にあり、使う際にそのコードをベースに改修することで導入できできます。

https://material-ui.com/

Material UI のサンプルは JS, Typescript などのサンプルがあり、
どのサンプルもよいコードばかりです。

css などの書き方も MaterialUIのコードがとても参考になると思います。

Step3 で作成したアプリを Material UI を使って便利にする。

その過程でMaterial UI のドキュメントやサンプルコードをたくさんみると思います。
そのなかでよりすすんだ書き方を習得できると思います。

Next Step

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

2020年のReact.js を公式チュートリアルをもとにマスター

Reactに難しいイメージが多いのか使える人を探しづらいという課題を感じています。

実際 Reactは長い歴史のなかで記述方法の変化、重要な機能が追加されつづけてきました。
そのすすんだ要素もふくめて、どうやって勉強するのがよいか、書いてみました。

基本的には公式のチュートリアルをうまく組み合わせて勉強すればよいと思います。

step.0 環境準備

何はともあれ、node をインストールする必要があります。
node.js は頻発に バージョンアップがはいるため、nvm で管理しましょう。

nvm

https://github.com/nvm-sh/nvm#installing-and-updating

nvm for windows の導入方法

https://docs.microsoft.com/ja-jp/windows/nodejs/setup-on-windows

Create React Appで勉強環境を構築

勉強をするためのアプリケーションを作れるようになりましょう。

Create React App は手軽に開発環境をつくれると同時に、
React開発のベストプラクティスがたくさんつめこまれています。

https://ja.reactjs.org/docs/create-a-new-react-app.html

Typescript はこちら
https://create-react-app.dev/docs/adding-typescript/

VSCodeの導入

https://code.visualstudio.com/

Step.1 React Tutorial の実施

まずは React の基本である State, Props, Event を下記のチュートリアルで体験しましょう。
https://ja.reactjs.org/tutorial/tutorial.html

わからないところは公式ドキュメントやGoogleで調べながらすすめます。

Step.2 React Hooks

次に現代のスタンダードになりつつある React Hooksを学びましょう。

React Hooks
https://ja.reactjs.org/docs/hooks-overview.html

概要を掴んだら、次は Step.1 のコードを React Hooks に書き直してみましょう。
その過程でわからなくったら公式ドキュメントを読みましょう。
終わる頃には半分ぐらいは理解できるようになっていると思います。

Step.3

次は TypeScript です。
使うか使わないかはわかれると思いますが、スタンダードになりつつあります。

TypeScript がはじめての人は下記をみてどうゆうものか把握します。5分で読めます。
英語なのでグーグル翻訳にかけると読みやすいです。

https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html

大枠掴んだら、React でどう使うのかを下記で把握します。

Typescript の書き方はバージョンアップやユーザのアイディアにより日々進化します。
react-typescript-cheatsheetにそのグッドプラクティスが集まっています。
少し難しくてもこのサイトの内容で覚えtmoraeru

React Typescript
https://react-typescript-cheatsheet.netlify.app/docs/basic/setup

Step.2 で作成した Hooks のアプリを TypeScript に書き換える

わからないところは react-typescript-cheatsheet を参考にしながらすすめます。

Step.4 Material UI

Material UI を学習します。
Material UI のサイトにはExampleが豊富にあり、使う際にそのコードをベースに改修することで導入できできます。

https://material-ui.com/

Material UI のサンプルは JS, Typescript などのサンプルがあり、
どのサンプルもよいコードばかりです。

css などの書き方も MaterialUIのコードがとても参考になると思います。

Step3 で作成したアプリを Material UI を使って便利にする。

その過程でMaterial UI のドキュメントやサンプルコードをたくさんみると思います。
そのなかでよりすすんだ書き方を習得できると思います。

Next Step

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

reactのaxiosでリクエスト失敗時の例外処理をいれる

catchで例外処理

        request
            .post("/getpodstatus", obj)
            // やりたい処理
            .then(request => {
                this.setState({
                    podstatus: request.data.items[0].status.phase
                });
                console.log(this.state.podstatus)
                }
            )

            // エラー時というかやりたい処理ができなかった場合の処理
            .catch(error => {
                this.setState({
                    podstatus: 'Stop'
                });
                console.log('POST失敗時の処理')
            })
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React+Firebaseでゲストユーザーログイン機能を実装しよう!!

今回は、ボタンをクリックすると簡単にログインできるゲストログイン機能を作成していきます。

ポートフォリオなどを作る際に、評価していただく方がわざわざformに入力する必要がないように、ゲストログインボタンは作っておいて損はないと思います。

それでは見ていきましょう?

前提?‍♂️

  • ReactとFirebaseでログイン機能を含むユーザー認証周りを作れるまたは既に完成している
  • ゲストログインに関して大まかな実装の方法を知りたい場合
  • 今回、firebaseの匿名認証は使いません

前提は上記の通りです。

FirebaseとReactでのログイン機能や会員登録機能についてわからないという場合は、以下のYouTube動画がおすすめです。

React Authentication Crash Course With Firebase And Routing

英語ではありますが、コードだけ見ていても多少は理解できると思います。

日本語解説については、Firebase React 認証などで検索すればいろいろ記事が出てきます。

実装?

さて、早速実装していきます。

と言っても、ログインの機能を既に作成済みであればとても簡単です。

考え方としては、あらかじめgest userを作成しておき、ボタンをクリックしたときに自動的にそのguest userのemailとpasswordを使ってログインが行われるようにするという感じです。

guest userのemailはguest@example.com、パスワードはpasswordとしてあらかじめアカウントを作成しておきます。

ログインの手段 違い
普通のログイン formにemailとpasswordを入力し、その値を使ってfirebaseのsignInWithEmailAndPasswordでログインする
ゲストログイン ボタンをクリックしたら、ゲストログインの関数を実行させ、signInWithEmailAndPasswordに変数ではなく、直接guest@example.comとpasswordを渡す

コードで説明した方がわかりやすいかもしれませんねw

hooksなどの定義やimport含め長くなりすぎるので省略しています。

Login.jsx
export default function Login() {

  const inputEmail = useCallback((event) => {
    setEmail(event.target.value)
  }, [setEmail]);

  const inputPassword = useCallback((event) => {
    setPassword(event.target.value)
  }, [setPassword]);

  //通常のログインの場合のログインボタンを押した後の処理
  const handleSubmit = async(e) {
    e.preventDefault()

    setError("")
    setLoading(true) 
    //login()の処理は別ファイルに記述しています。
    //loginではsignInWithEmailAndPasswordでログイン処理をしています。
    return login(email, password)
    .then(() => {
      history.push("/")
    })
    .catch((error) => {
      setError("failed!!")
    })
    .finally(() => {
        setLoading(false)
    });
  }

  //以下ゲストログイン用の処理(ほぼ通常のログインと同じです)
  const onClickGuestButton = () => {
    setError("")
    setLoading(true)
    //loginの処理で直接emailとpasswordをloginに渡しています。
    return login("guest@example.com", "password")
    .then(() => {
      history.push("/")
    })
    .catch((error) => {
      setError("failed!!")
    })
    .finally(() => {
        setLoading(false)
    });
  }
return(
  <>
  {/*ここにformのコードがあります*/}

 {/*ここからがゲストログインボタン。formの外に書いてます*/}
    <Button onClick={onClickGuestButton}>
      ゲストユーザーとしてログイン
    </Button>
  </>
)

以下は、loginの処理が書いてあるAuthContext.jsxです

AuthContext.jsx
  const login = (email, password) => {
    return auth.signInWithEmailAndPassword(email, password)
  }

signInWithEmailAndPasswordはfirebaseのメソッドです。

これで実装完了です?

最後に?

いかがだったでしょうか?

ログイン機能を既に実装できているのであれば、ゲストログインボタンは結構簡単にできるものですよね。

作っておいて損はないです?‍♂️

もし、役に立ったと感じていただけたらLGTMお願いします!!?

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

Material-UIを使ってダイアログを表示するには?

Material-UIを使ってダイアログを表示するには一筋縄ではいかずパラメータを渡す必要があります。
なので今回は「Material-UIを使ってダイアログを表示する方法」を紹介します。

ダイアログ表示.gif

この機能は3つのファイルで構成されています。

  1. 一覧画面(index.jsx)
  2. アイテムコンポーネント(item.jsx)
  3. ダイアログコンポーネント(dialog.jsx)

1.一覧画面

WS000000.JPG

index.jsx
import React from 'react';
import Box from '@material-ui/core/Box';
import Item from "@comp/item";
import Grid from '@material-ui/core/Grid';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
  gridItem: {
    marginTop: theme.spacing(2),
  },
}));

// 表示するアイテムのリスト
const bookList = [
  {
    id: 1,
    title: "ファスト&スロー",
    page: 100,
    image: "http://books.google.com/books/content?id=tNDza_Pb0UMC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api"
  },
  {
    id: 2,
    title: "ファストフードの恐ろしい話",
    page: 200,
    image: "http://books.google.com/books/content?id=ghgzDwAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api"
  },
  {
    id: 3,
    title: "英語でおもてなし・ファストフード食べ歩き",
    page: 300,
    image: "http://books.google.com/books/content?id=HC-gCAAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api"
  },
]

export default function Index(props) {
  const classes = useStyles();
  const compProps = {
    gridItem: {
      item         : true,
      space        : 5,
      xs           : 12,
      md           : 6,
      lg           : 3,
      className    : classes.gridItem
    }
  }

  return (
    <>
      <Box mx={10} mt={4}>
        <Box display="flex" alignItems="center">
          <h2>{props.title}</h2>
        </Box>
        <Grid container spacing={1}>
          {bookList.map(item =>
            <Grid {...compProps.gridItem} key={item.id}>
              <Item 
                bookParam={item}
                />
            </Grid>
          )}
        </Grid>
      </Box>
    </>
  );
}


一覧画面(index.jsx)では定義したリストを順番に並べて表示しています。

まずは、表示するリストのデータを定義しています。通常はAPIやDBから取得したりしますが、今回は見本なので直接定義しています。

そして、定義したリストデータをループで回して各アイテムをBookCardというコンポーネントで表示しています。各データはbookParamという名前でBookCardコンポーネントに渡しています。

2.アイテム

WS000001.JPG

item.jsx
import React, {useState} from 'react';
import Dialog  from "@comp/dialog";
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Link from '@material-ui/core/Link';

const item = (props) => {

  /** 画面パラメータ */
  const [dialogOpenF, setDialogOpenF] = useState(false);

  /**
   * コンポーネントに渡す引数
   */
  const compProps = {
    bookShowDialog: {
      open : dialogOpenF,                     //ダイアログの表示プローアティ
      bookParam: props.bookParam,
      onClose: () => setDialogOpenF(false),   //ダイアログ非表示処理
    },
    showLink: {
      href: "#",
      onClick: () => setDialogOpenF(true)     //ダイアログ表示処理
    }
  }

  return (
    <Box>
      {/* 画像の表示 */}
      <Link {...compProps.showLink}>
        <img src={props.bookParam.image}/>
      </Link>

      {/* タイトルの表示 */}
      <Typography>
        <Link {...compProps.showLink}>
        {props.bookParam.title}
        </Link>
      </Typography>

      {/* ページ数の表示 */}
      <Typography>{props.bookParam.page}ページ</Typography>

      {/* ダイアログの表示 */}
      <Dialog {...compProps.bookShowDialog} />
    </Box>
  );
}

export default item;

各アイテム(item.jsx)では呼び出し元から渡されたデータの表示とダイアログの表示・非表示の処理を行っています

引き渡されたパラメータはprops.bookParamに格納されているため画像URL(image)、タイトル(title)、ページ数(page)をそのまま出力に利用しています。

そして、最後のDialogコンポーネントに各アイテムの情報を渡しています。

2-1.ダイアログの表示・非表示

ダイアログ表示・非表示用のパラメータdialogOpenFを用意しています。このパラメータをtrue・falseに更新することでダイアログの表示・非表示に切り替えているわけです。

後は画像とタイトルがクリックされたときにdialogOpenFを更新するメソッドsetDialogOpenFを使ってtrueに変更しています。これによりopenプロパティーが更新されダイアログが表示されます。

そして、ダイアログが閉じる処理を行ったときにsetDialogOpenFでfalseを指定してダイアログを非表示に更新しています。

3.ダイアログ

WS000002.JPG

dialog.jsx
import React from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import PropTypes from 'prop-types';
import Typography from '@material-ui/core/Typography';

const dialog = (props) => {

  const compProps = {
    dialog: {
      open: props.open,           //ダイアログの表示・非表示プロパティ
      onClose: props.onClose,     //ダイアログが閉じられた時の処理
      scroll:'paper',
    },
    dialogContent: {
      dividers: true
    },
    dialogContentText: {
      tabIndex: -1
    },
    closeButton: {
      onClick: props.onClose,     //閉じるボタンが押されたときの処理
      color: "primary"
    },
  }

  return (
      <Dialog {...compProps.dialog}>
        {/* タイトル */}
        <DialogTitle>{props.bookParam.title}</DialogTitle>

        {/* 画像とページ数 */}
        <DialogContent {...compProps.dialogContent}>
          <img src={props.bookParam.image} />
          <DialogContentText {...compProps.dialogContentText}>
            <Typography>{props.bookParam.page}ページ</Typography>
          </DialogContentText>
        </DialogContent>

        {/* 閉じるボタン */}
        <DialogActions>
          <Button {...compProps.closeButton}>
            閉じる
          </Button>
        </DialogActions>
      </Dialog>
  );
}

dialog.propTypes = {
    open: PropTypes.bool,       //表示フラグ
    onClose: PropTypes.func,    //閉じる処理
  }

  export default dialog;

最後にダイアログに呼び出し元から渡された情報を出力します。

DialogTitleにはアイテムのタイトル。DialogContentにはアイテムの画像とページ数を表示。そして、ダイアログを閉じる処理を行う「閉じるボタン」を定義します。

閉じるボタンが押されたときは呼び出し元から引き渡されたprops.onCloseを利用します。これにより、呼び出し元からダイアログを非表示にします。

また、propTypesで呼び出し元からのパラメータの型をチェックしまています。openはダイアログの表示・非表示でboolean型。onCloseは閉じる処理でfunc型。これにより異常な値が渡ってこれば警告が表示されるようになっているのです。

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

キャッシュとクッキーの違い(メモ)

共通点と違い

共通点
どちらもブラウザ訪問後に情報を一時的に保存することができる。
違い
保存する情報の内容が違う

それぞれの特徴

キャッシュ
訪れたウェブページの情報を一時的に保存する。 これにより訪れるページの読み込みが速くなる。
クッキー
ページに訪れたユーザーの情報を一時的に保存する。 例えば、一度パスワードを打ってログインしたサイトに再度訪れてもログイン状態になっているのはクッキーのおかげ。

キャッシュのデメリットを覚えておこう

  1. ウェブページの最新情報反映について。キャッシュデータはキャッシュした時点のデータなのでウェブページの更新があっても最新のものに切り替わらないため一度古いキャッシュを削除する必要がある。
  2. キャッシュによってPCやスマホの容量が食われてしまうことがあるので注意が必要。

参考記事

https://time-space.kddi.com/ict-keywords/20191210/2798

余談

react native でsimulatorを使いながら開発を進めていた際に、何度かキャッシュを削除することでエラーが直ることがありました。

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

React-leafletの使い方メモ

React-leafletとは

Open Source Mapを表示するJavaScriptライブラリであるleafletを、React.js上で使えるように拡張するライブラリです。

React Leaflet公式
https://react-leaflet.js.org/

本記事は公式のチュートリアルを順番にやっていくだけですので詳しくは公式サイトを参照してください
https://react-leaflet.js.org/docs/start-introduction

1. プロジェクトの作成

terminal
mkdir react-leaflet
cd react-leaflet
npx create-react-app my-app

2. インストール

今回は開発版をインストールしました

開発版をインストールする場合
npm install leaflet react-leaflet@next --save
安定版をインストールする場合
npm install leaflet react-leaflet --save

3. 地図を表示

こちらのコードを参考にやっていきます
https://qiita.com/sugasaki/items/d225cf548e9a787dbd9c

地図を表示する為のクラスを作成

MapContainerで地図オブジェクトをつくり、TileLayerで地図タイルを取得できるようにすると地図が表示されます。

MapContainerがL.map()、TileLayerがL.tileLayer()、MarkerがL.marker()に対応していますので、leaflet触ったことがある方はイメージしやすいと思います。

my-app/component/simple.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'

export default class SimpleExample extends Component {
  render() {
    const position = [51.505, -0.09];
    return (
      <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <Marker position={position}>
          <Popup>
            A pretty CSS3 popup. <br /> Easily customizable.
          </Popup>
        </Marker>
      </MapContainer>
    )
  }
}

App.jsでSimpleExampleクラスを呼ぶように変更

my-app/App.js
import Leaflet from 'leaflet'
import React, { Component } from 'react';
import './App.css';
import 'leaflet/dist/leaflet.css';
import SimpleExample from './components/simple'

Leaflet.Icon.Default.imagePath =
  '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/images/'

class App extends Component {
  render() {
    return (
      <SimpleExample />
    );
  }
}

export default App;

leafletコンテナの表示幅を指定

指定し忘れると高さゼロ(=表示されない)になってしまうので必ず設定します

my-app/index.js
.leaflet-container {
  width: 600px;
  height: 300px;
  margin: 10px;

サーバーを起動して確認

サーバーを起動します

terminal
npm start

以上でhttp://localhost:3000/ に地図が表示される筈です
image.png

4. イベント管理

useMapEvents()によりleafletのイベントハンドラを使うことができます。以下ではclick()イベントが発生したら端末の現在位置を取得してMarkerを置くという動作を追加しています。

my-app/components/event.js
import React, { Component, useState } from 'react'
import { MapContainer, TileLayer, useMapEvents, Marker, Popup } from 'react-leaflet'


function LocationMarker() {
    const [position, setPosition] = useState(null)
    const map = useMapEvents({
      click() {
        map.locate()
      },
      locationfound(e) {
        setPosition(e.latlng)
        map.flyTo(e.latlng, map.getZoom())
      },
    })

    return position === null ? null : (
      <Marker position={position}>
        <Popup>You are here</Popup>
      </Marker>
    )
  }

export default class EventsExample extends Component {
  render() {
    const position = [51.505, -0.09];
    return (
      <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <LocationMarker />
      </MapContainer>
    )
  }
}
my-app/App.js
import Leaflet from 'leaflet'
import React, { Component } from 'react';
import './App.css';
import 'leaflet/dist/leaflet.css';
import SimpleExample from './components/simple'
import EventsExample from './components/events'

Leaflet.Icon.Default.imagePath =
  '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/images/'

class App extends Component {
  render() {
    return (
      <div>
        SimpleExample
        <SimpleExample />

        EventsExample
        <EventsExample />
      </div>
    );
  }
}

export default App;

image.png

5. ポリゴンを表示

ポリゴンも簡単に追加できます

my-app/src/components/polygon.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, Popup, Circle, CircleMarker, Polyline, Polygon, Rectangle } from 'react-leaflet'

export default class PolygonExample extends Component {
  render() {
    const center = [51.505, -0.09];

    const polyline = [
      [51.505, -0.09],
      [51.51, -0.1],
      [51.51, -0.12],
    ];

    const multiPolyline = [
      [
        [51.5, -0.1],
        [51.5, -0.12],
        [51.52, -0.12],
      ],
      [
        [51.5, -0.05],
        [51.5, -0.06],
        [51.52, -0.06],
      ],
    ];

    const polygon = [
      [51.515, -0.09],
      [51.52, -0.1],
      [51.52, -0.12],
    ];

    const multiPolygon = [
      [
        [51.51, -0.12],
        [51.51, -0.13],
        [51.53, -0.13],
      ],
      [
        [51.51, -0.05],
        [51.51, -0.07],
        [51.53, -0.07],
      ],
    ];

    const rectangle = [
      [51.49, -0.08],
      [51.5, -0.06],
    ];

    const fillBlueOptions = { fillColor: 'blue' };
    const blackOptions = { color: 'black' };
    const limeOptions = { color: 'lime' };
    const purpleOptions = { color: 'purple' };
    const redOptions = { color: 'red' };

    return (
      <MapContainer center={center} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <Circle center={center} pathOptions={fillBlueOptions} radius={200} />
        <CircleMarker center={[51.51, -0.12]} pathOptions={redOptions} radius={20}>
          <Popup>Popup in CircleMarker</Popup>
        </CircleMarker>
        <Polyline pathOptions={limeOptions} positions={polyline} />
        <Polyline pathOptions={limeOptions} positions={multiPolyline} />
        <Polygon pathOptions={purpleOptions} positions={polygon} />
        <Polygon pathOptions={purpleOptions} positions={multiPolygon} />
        <Rectangle bounds={rectangle} pathOptions={blackOptions} />
      </MapContainer>
    )
  }
}

image.png

6. SVG overlay

SVGを上に重ねることもできます

my-app/src/components/svg.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, SVGOverlay } from 'react-leaflet'

export default class SVGExample extends Component {
  render() {
    const position = [51.505, -0.09];
    const bounds = [
      [51.49, -0.08],
      [51.5, -0.06],
    ];
    return (
      <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
      <TileLayer
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <SVGOverlay attributes={{ stroke: 'red' }} bounds={bounds}>
        <rect x="0" y="0" width="100%" height="100%" fill="blue" />
        <circle r="5" cx="10" cy="10" fill="red" />
        <text x="50%" y="50%" stroke="white">
          text
        </text>
      </SVGOverlay>
    </MapContainer>
    )
  }
}

image.png

7. LayerGroupとFeatureGroup

leafletにあるLayerGroupとかFeatureGroupも使えます

my-app/src/components/group.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, Popup, LayerGroup, Circle, FeatureGroup, Rectangle } from 'react-leaflet'

export default class LayerGroupExample extends Component {
  render() {
    const center = [51.505, -0.09];
    const rectangle = [
      [51.49, -0.08],
      [51.5, -0.06],
    ];
    const fillBlueOptions = { fillColor: 'blue' };
    const fillRedOptions = { fillColor: 'red' };
    const greenOptions = { color: 'green', fillColor: 'green' };
    const purpleOptions = { color: 'purple' };

    return (
      <MapContainer center={center} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <LayerGroup>
          <Circle center={center} pathOptions={fillBlueOptions} radius={200} />
          <Circle
            center={center}
            pathOptions={fillRedOptions}
            radius={100}
            stroke={false}
          />
          <LayerGroup>
            <Circle
              center={[51.51, -0.08]}
              pathOptions={greenOptions}
              radius={100}
            />
          </LayerGroup>
        </LayerGroup>
        <FeatureGroup pathOptions={purpleOptions}>
          <Popup>Popup in FeatureGroup</Popup>
          <Circle center={[51.51, -0.06]} radius={200} />
          <Rectangle bounds={rectangle} />
        </FeatureGroup>
      </MapContainer>
    )
  }
}

image.png

8. マウスオーバーで注釈を出す

MarkerやCircleにTooltipを追加しておくと注釈を出すことが出来ます

my-app/src/components/tooltips.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, Marker, Popup, Circle, CircleMarker, Polygon, Rectangle, Tooltip } from 'react-leaflet'
import { useState, useMemo } from 'react'

const center = [51.505, -0.09]

const multiPolygon = [
  [
    [51.51, -0.12],
    [51.51, -0.13],
    [51.53, -0.13],
  ],
  [
    [51.51, -0.05],
    [51.51, -0.07],
    [51.53, -0.07],
  ],
];

const rectangle = [
  [51.49, -0.08],
  [51.5, -0.06],
];

function TooltipCircle() {
  const [clickedCount, setClickedCount] = useState(0)
  const eventHandlers = useMemo(
    () => ({
      click() {
        setClickedCount((count) => count + 1)
      },
    }),
    [],
  )

  const clickedText =
    clickedCount === 0
      ? 'Click this Circle to change the Tooltip text'
      : `Circle click: ${clickedCount}`

  return (
    <Circle
      center={center}
      eventHandlers={eventHandlers}
      pathOptions={{ fillColor: 'blue' }}
      radius={200}>
      <Tooltip>{clickedText}</Tooltip>
    </Circle>
  )
}

export default class ToolTipsExample extends Component {
  render() {
    return (
      <MapContainer center={center} zoom={13} scrollWheelZoom={false}>
      <TileLayer
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <TooltipCircle />
      <CircleMarker
        center={[51.51, -0.12]}
        pathOptions={{ color: 'red' }}
        radius={20}>
        <Tooltip>Tooltip for CircleMarker</Tooltip>
      </CircleMarker>
      <Marker position={[51.51, -0.09]}>
        <Popup>Popup for Marker</Popup>
        <Tooltip>Tooltip for Marker</Tooltip>
      </Marker>
      <Polygon pathOptions={{ color: 'purple' }} positions={multiPolygon}>
        <Tooltip sticky>sticky Tooltip for Polygon</Tooltip>
      </Polygon>
      <Rectangle bounds={rectangle} pathOptions={{ color: 'black' }}>
        <Tooltip direction="bottom" offset={[0, 20]} opacity={1} permanent>
          permanent Tooltip for Rectangle
        </Tooltip>
      </Rectangle>
      </MapContainer>
    )
  }
}

image.png

9. LayersControl

レイヤーを選んで表示するようにすることもできます

my-app/src/components/leyersControl.js
import React, { Component } from 'react'
import { MapContainer, TileLayer, Marker, Popup, LayersControl, LayerGroup, Circle, FeatureGroup, Rectangle } from 'react-leaflet'

export default class LayersControlExample extends Component {
  render() {
    const center = [51.505, -0.09];
    const rectangle = [
      [51.49, -0.08],
      [51.5, -0.06],
    ];

    return (
      <MapContainer center={center} zoom={13} scrollWheelZoom={false}>
        <LayersControl position="topright">
          <LayersControl.BaseLayer checked name="OpenStreetMap.Mapnik">
            <TileLayer
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
              url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            />
          </LayersControl.BaseLayer>
          <LayersControl.BaseLayer name="OpenStreetMap.BlackAndWhite">
            <TileLayer
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
              url="https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png"
            />
          </LayersControl.BaseLayer>
          <LayersControl.Overlay name="Marker with popup">
            <Marker position={center}>
              <Popup>
                A pretty CSS3 popup. <br /> Easily customizable.
              </Popup>
            </Marker>
          </LayersControl.Overlay>
          <LayersControl.Overlay checked name="Layer group with circles">
            <LayerGroup>
              <Circle
                center={center}
                pathOptions={{ fillColor: 'blue' }}
                radius={200}
              />
              <Circle
                center={center}
                pathOptions={{ fillColor: 'red' }}
                radius={100}
                stroke={false}
              />
              <LayerGroup>
                <Circle
                  center={[51.51, -0.08]}
                  pathOptions={{ color: 'green', fillColor: 'green' }}
                  radius={100}
                />
              </LayerGroup>
            </LayerGroup>
          </LayersControl.Overlay>
          <LayersControl.Overlay name="Feature group">
            <FeatureGroup pathOptions={{ color: 'purple' }}>
              <Popup>Popup in FeatureGroup</Popup>
              <Circle center={[51.51, -0.06]} radius={200} />
              <Rectangle bounds={rectangle} />
            </FeatureGroup>
          </LayersControl.Overlay>
        </LayersControl>
      </MapContainer>
    )
  }
}

image.png

10. stateの値でRectangleの色を変える

この辺からleaflet単独ではなくReactからleafletを操作する手順になります。ここではEffectフックでRectangleの色を変えています。

my-app/src/components/panes.js
import React, { Component, useState, useRef, useEffect } from 'react'
import { MapContainer, TileLayer, Pane, Rectangle } from 'react-leaflet'

const outer = [
  [50.505, -29.09],
  [52.505, 29.09],
]
const inner = [
  [49.505, -2.09],
  [53.505, 2.09],
]

function BlinkingPane() {
  const [render, setRender] = useState(true)
  const timerRef = useRef()
  useEffect(() => {
    timerRef.current = setInterval(() => {
      setRender((r) => !r)
    }, 1000)
    return () => {
      clearInterval(timerRef.current)
    }
  }, [])

  return render ? (
    <Pane name="cyan-rectangle" style={{ zIndex: 500 }}>
      <Rectangle bounds={outer} pathOptions={{ color: 'cyan' }} />
    </Pane>
  ) : null
}

export default class PanesExample extends Component {
  render() {
    return (
      <MapContainer bounds={outer} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <BlinkingPane />
        <Pane name="yellow-rectangle" style={{ zIndex: 499 }}>
          <Rectangle bounds={inner} pathOptions={{ color: 'yellow' }} />
          <Pane name="purple-rectangle">
            <Rectangle bounds={outer} pathOptions={{ color: 'purple' }} />
          </Pane>
        </Pane>
      </MapContainer>
    )
  }
}

横に長いRectangleが紫⇔青と交互に色を変えながら表示されます
image.png

11. Drag可能なMarker

MarkerをDraggableにするかどうかをReact stateにしておいて、途中でDrag可能にしたりDragできなくしたりする例です

my-app/src/components/draggable.js
import React, { Component, useState, useRef, useMemo, useCallback } from 'react'
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'

const center = {
  lat: 51.505,
  lng: -0.09,
}

function DraggableMarker() {
  const [draggable, setDraggable] = useState(false)
  const [position, setPosition] = useState(center)
  const markerRef = useRef(null)
  const eventHandlers = useMemo(
    () => ({
      dragend() {
        const marker = markerRef.current
        if (marker != null) {
          setPosition(marker.getLatLng())
        }
      },
    }),
    [],
  )
  const toggleDraggable = useCallback(() => {
    setDraggable((d) => !d)
  }, [])

  return (
    <Marker
      draggable={draggable}
      eventHandlers={eventHandlers}
      position={position}
      ref={markerRef}>
      <Popup minWidth={90}>
        <span onClick={toggleDraggable}>
          {draggable
            ? 'Marker is draggable'
            : 'Click here to make marker draggable'}
        </span>
      </Popup>
    </Marker>
  )
}

export default class DraggableMarkerExample extends Component {
  render() {
    return (
      <MapContainer center={center} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <DraggableMarker />
      </MapContainer>
    )
  }
}

image.png

12. View Bounds

innerBoundsまたはouterBoundsの内側をクリックすると、クリックした側をinnerHandler、クリックしていない側をouterHandlerに登録して、その範囲が大きく表示されるように

my-app/src/components/viewBounds.js
import React, { Component, useState, useMemo } from 'react'
import { MapContainer, TileLayer, Rectangle, useMap } from 'react-leaflet'

const innerBounds = [
  [49.505, -2.09],
  [53.505, 2.09],
]
const outerBounds = [
  [50.505, -29.09],
  [52.505, 29.09],
]

const redColor = { color: 'red' }
const whiteColor = { color: 'white' }

function SetBoundsRectangles() {
  const [bounds, setBounds] = useState(outerBounds)
  const map = useMap()

  const innerHandlers = useMemo(
    () => ({
      click() {
        setBounds(innerBounds)
        map.fitBounds(innerBounds)
      },
    }),
    [map],
  )
  const outerHandlers = useMemo(
    () => ({
      click() {
        setBounds(outerBounds)
        map.fitBounds(outerBounds)
      },
    }),
    [map],
  )

  return (
    <>
      <Rectangle
        bounds={outerBounds}
        eventHandlers={outerHandlers}
        pathOptions={bounds === outerBounds ? redColor : whiteColor}
      />
      <Rectangle
        bounds={innerBounds}
        eventHandlers={innerHandlers}
        pathOptions={bounds === innerBounds ? redColor : whiteColor}
      />
    </>
  )
}

export default class ViewBoundsExample extends Component {
  render() {
    return (
      <MapContainer bounds={outerBounds} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <SetBoundsRectangles />
      </MapContainer>
    )
  }
}

image.png

13. Animated Panning

checkboxの値でsetViewのanimateを有効にしたり無効にしたりできます

my-app/src/components/animatedPanning.js
import React, { Component, useRef } from 'react'
import { MapContainer, TileLayer, useMapEvent } from 'react-leaflet'

function SetViewOnClick({ animateRef }) {
  const map = useMapEvent('click', (e) => {
    map.setView(e.latlng, map.getZoom(), {
      animate: animateRef.current || false,
    })
  })

  return null
}

function AnimateExample() {
  const animateRef = useRef(false)

  return (
    <>
      <p>
        <label>
          <input
            type="checkbox"
            onChange={() => {
              animateRef.current = !animateRef.current
            }}
          />
          Animate panning
        </label>
      </p>
      <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <SetViewOnClick animateRef={animateRef} />
      </MapContainer>
    </>
  )
}

export default class AnimatedPanningExample extends Component {
  render() {
    return (
      <AnimateExample />
    )
  }
}

14. External State

逆にleafletの情報をコンポーネント外で使うことも出来ます

my-app/src/components/
import React, {useState, useCallback, useEffect, useMemo} from 'react'
import { MapContainer, TileLayer } from 'react-leaflet'

const center = [51.505, -0.09]
const zoom = 13

function DisplayPosition({ map }) {
  const [position, setPosition] = useState(map.getCenter())

  const onClick = useCallback(() => {
    map.setView(center, zoom)
  }, [map])

  const onMove = useCallback(() => {
    setPosition(map.getCenter())
  }, [map])

  useEffect(() => {
    map.on('move', onMove)
    return () => {
      map.off('move', onMove)
    }
  }, [map, onMove])

  return (
    <p>
      latitude: {position.lat.toFixed(4)}, longitude: {position.lng.toFixed(4)}{' '}
      <button onClick={onClick}>reset</button>
    </p>
  )
}

function ExternalStateExample() {
  const [map, setMap] = useState(null)

  const displayMap = useMemo(
    () => (
      <MapContainer
        center={center}
        zoom={zoom}
        scrollWheelZoom={false}
        whenCreated={setMap}>
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
      </MapContainer>
    ),
    [],
  )

  return (
    <div>
      {map ? <DisplayPosition map={map} /> : null}
      {displayMap}
    </div>
  )
}

export default ExternalStateExample;

image.png

まとめ

Reactを使う環境でleafletを使うなら単独で使うよりこちらを使った方が良さげです

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

React躓きメモ

Async callback was not invoked within the 5000ms timeout specified by jest.setTimeoutでjestのテストが上手くいかないケース

it("テストネーム", async () => {
  // settimeoutを繰り返すようなテスト
}, 時間);

上記のように、itの第3引数に時間を入れることで上手くいくらしい

useEffectがstateなどで何度もレンダリングされてしまう。

https://qiita.com/k-penguin-sato/items/9373d87c57da3b74a9e
以下のように第2引数をから配列にすればいいらしい。

useEffect(() => {
// 処理
}, []);

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

静的サイトジェネレーター Gatsby.js 入門

Gatsby.js入門

第1回は、Gatsby.jsのインストールの仕方について書いていこうと思います

Gatsby.jsを利用するための準備

開発環境の用意
・Node.js(nvm)
・yarn
・git

のインストールが必要です
インストールの仕方については、次の記事を参照してください

Gatsby CLIをインストールする

yarn global add gatsby-cli
と入力して、Gatsby.jsをインストールしていきます。

スターターをダウンロードしてみる

gatsby new
と入力して、サイトを構築していきます。

入力すると、プロジェクト名と使用するスターターを聞いてくるので、自分にあったプロジェクト名とスターターを選んであげましょう。

※スターターについては、https://www.gatsbyjs.org/starters/?v=2
を参照してみてください

ダウンロードできたら、cd プロジェクト名でプロジェクトフォルダに移動し、gatsby developで開発サーバーを起動してみます。

開発サーバーが起動したら、URLからページに飛んでページの表示を確認してみましょう!

サイトを公開する

gatsby buildと入力するとpublic/フォルダにサイトのデータができあがります。
これを公開すればサイトの公開は完了です。

※ちなみにビルドしたサイトの表示を確認する場合、gatsby serveと入力するば、ビルドしたサイトを確認することが可能です。

参考文献:Webサイト高速化のための静的サイトジェネレーター活用入門 GatsbyJSで実現する高速&実用的なサイト構築

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