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

ReactでGoogleMapAPIの使い方

はじめに

ReactでGoogleマップを表示させる方法をまとめました。
Reactのライブラリである@react-google-maps/apiを利用します。
GoogleMapのAPIキーは事前に取得及び有効化をお願い致します。

参考サイト

こちらの記事を参考にさせていただきました。
https://medium.com/@allynak/how-to-use-google-map-api-in-react-app-edb59f64ac9d
日本語でググってイマイチだった場合は、英語でググるといい記事が見つかることがよくあります。
自分はいつも、How to use "キーワード" in "言語" みたいな感じで検索します。
今回だったら How to use Google Map in Reactで検索しました。

導入方法

まずは@react-google-maps/apiを利用できるようにターミナルで準備します。
create-react-appで作成したプロジェクトのディレクトリで、
とりあえず自分はいつも最新バージョンを調べます。

$ npm info @react-google-maps/api

結果の一部がこちら↓

@react-google-maps/api@2.1.0 | MIT | deps: 4 | versions: 98
React.js Google Maps API integration
https://react-google-maps-api-docs.netlify.app/

今現在のの最新バージョンは@react-google-maps/api@2.1.0だったのでこれをyarn addします。

$ yarn add @react-google-maps/api@2.1.0

一番下にDone in 何秒と出てきたら成功です。
これでこのプロジェクトで@react-google-maps/apiのライブラリが利用できるようになりました。

Mapの表示

まずはライブラリから必要なものをImportします。

Map.js
import React from 'react';
import { GoogleMap, LoadScript } from '@react-google-maps/api';

中はアロー関数を使って書きました。

Map.js
const Map = () => {
        const mapStyles = {
        height: "100%",
        width: "100%"
    };

    const defaultCenter = {
        lat: 41.3851, lng: 2.1734
    }

    return (
        <LoadScript
            googleMapsApiKey='YOUR_API_KEY'>
            <GoogleMap
                mapContainerStyle={mapStyles}
                zoom={13}
                center={defaultCenter}
            />
        </LoadScript>
    )
}

mapStylesの中のstyleを変えれば地図の形が変わりますし、defaultCenterのlatとlng(緯度と経度)を変えれば初期表示の中心地を変えれます。
YOUR_API_KEYをご自身のGoogleMapのAPIキーに変更してください。
ここまででMapが表示されたかと思います。
スクリーンショット 2021-01-14 12.57.51.png

Markerの表示

次に指定した場所にピンを立ててみようと思います。
ピンもライブラリからimportが必要です。
先ほどのimportにMarkerも追加します。

Map.js
import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api';

そしてピンを立てる場所を指定します。
今回は2箇所を配列にいれてあげます。

Map.js
const locations = [
        {
            name: "Location 1",
            location: {
                lat: 41.3954,
                lng: 2.162
            },
        },
        {
            name: "Location 2",
            location: {
                lat: 41.3917,
                lng: 2.1649
            },
        },
    ];

最後にGoogleMapの間にMarker情報入れてあげます。
map関数を使用して、上で指定したlocationsの情報を一つずつitemに入れていくイメージです。

Map.js
            <GoogleMap
                mapContainerStyle={mapStyles}
                zoom={13}
                center={defaultCenter}>
                {
                    locations.map(item => {
                        return (
                            <Marker key={item.name} position={item.location} />
                        )
                    })
                }
            </GoogleMap>

これでピンが立ったはずです。
スクリーンショット 2021-01-14 23.29.37.png

infoWindowの表示

最後にinfoWindowを表示させたいと思います。
またライブラリからimport
今回はInfoWindowだけでなくuseStateも使うので併せてimportします。

Map.js
import React, { useState } from 'react';
import { GoogleMap, LoadScript, Marker, InfoWindow } from '@react-google-maps/api';

ピンをクリックしたら表示されるようにしたいので、関数を作っておきます。

Map.js
const [selected, setSelected] = useState({});

const onSelect = item => {
        setSelected(item);
      }

クリックするとonSelectが実行され、selectedの配列にitemの情報が入ります。

Map.js
                {
                    locations.map(item => {
                        return (
                            <Marker key={item.name} position={item.location}
                                onClick={() => onSelect(item)}
                            />
                        )
                    })
                }
                {
                    selected.location &&
                    (
                        <InfoWindow
                            position={selected.location}
                            clickable={true}
                            onCloseClick={() => setSelected({})}
                        >
                            <p>{selected.name}</p>
                        </InfoWindow>
                    )
                }

こんな感じで表示されましたでしょうか?
スクリーンショット 2021-01-14 23.36.41.png

終わりに

GoogleMapの使い方は他にもあるかと思いますが、個人的にはライブラリを使用するのが楽かなと思いました。
もしかしたらライブラリを使用することによって制限されるものがあるかもしれませんので、発見したらまた共有させていただこうと思います。
最後まで読んでいただきありがとうございました。

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

【Tailwind + React】NavLinkを使ってactiveクラスをスタイリングする方法

NavLink利用時の問題点

・通常時のテキストカラーは黒
・アクティブ時はテキストカラーが白、背景色は黒

以上のようにスタイリングを行いたい場合、activeClassNameを使用てもスタイリングが上書きされない

react.jsx
          <NavLink
            className={`inline-block px-4 py-2 rounded-full font-semibold text-sm text-black`}
            activeClassName={`bg-black text-white`}
            to={`/blogs/${user.uid}`}
          >
            ブログ
          </NavLink>

解決方法: @applyを使用してスタイリングを行う

Tailwind導入の際に作成したindex.css内で@applyを作成してスタイリングをあてる。

index.css
/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

a.active {
  @apply bg-black text-white;
}

(参考画像)
image.png

以上のようにaタグのclass内にactiveが付与される。
a.activeと指定することで、activeが付与された際のスタイリングを当てることが可能。

また、その際はNavLinkactiveClassNameは使用しないので削除してOK。

react.jsx
 // 通常時(inactive)のスタイリングがベースとなる
          <NavLink
            className={`inline-block px-4 py-2 rounded-full font-semibold text-sm text-black`}
            to={`/blogs/${user.uid}`}
          >
            ブログ
          </NavLink>

参考リンク

Conflicting className precedence rules #1010
Tailwindドキュメントのindex.css

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

【React】メルカリ出品に便利な説明文メーカーを作ってみた

はじめまして!

Djangoでウェブアプリらしきもの作ったり、RESTAPI作ったりしていたのですが、
フロントエンドもやろうと思い、Reactを前にはじめました(DjangoのAPIとつなげたアプリも自作中です。後に記事にします。)

作ったきっかけは、

  • メルカリで本を売るときにトラブルを起こさない
  • かんたんに必要な情報を提供する
  • ツールとして少なくとも自分が使う

これだけです。

リンク↓(後にドメインはとります。)
https://github.com/cota-eng/mertool
https://xenodochial-shannon-305440.netlify.app/

使用技術など

  • React(Hooks,useState,useEffect,useRef etc)
  • TypeScript
  • Netlify
  • Material ui

です。どうせならTypescriptも使おうという感じです。

要件定義らしきこと

必要なことをまとめました。

  • 新刊購入したものかどうか
  • 表紙の傷の程度
  • 折れ・書き込みの有無と種類
  • タバコ・ペットなどアレルギーを引き起こすものの有無

これらさえ伝われば、メルカリで本を売るときのトラブルはなくなるはずです。(あとは写真次第)

設計らしきこと

一応、親子のコンポーネントだけ作りました。

App.tsxに7つのコンポーネントを紐付け、propsで生成する文章のみ管理しました。

作るときに迷ったこと

TypeScriptでの型定義において、propsでも型定義をするのが斬新で、最初は迷いました。

また、イベントごとに型が決まっているのも、ググりながらなんとか解決できました。

ちょこちょこやって、合計4日くらいかかったように思います。

実装したいことをコードに落とし込む

新刊購入したものかどうかを扱うのはシンプルでした。例えば以下のような感じ。

const handleIsNew = () => {
    setIsNew(!isNew);
  };
  const [isNew, setIsNew] = useState<boolean>(true);
  const isNewText: string = isNew
    ? "新品で購入した本です。\n\n"
    : "新品で購入したものではありません。\n\n";
  useEffect(() => {
    props.setIsNewText(isNewText);
  }, [isNewText, props]);

しかし、ペットの種類を選び、それを文章として親要素に渡す方法について迷いがありました。

正直、最適なコードではないですが、とりあえず完成させたかったのでゴリ押ししました。

ペットの有無を選択させるのは、

  • ペットがいない
  • 犬がいる
  • 猫がいる
  • その他の動物

の4つのcheckboxで、その条件は、

  • ペットがいないを選択したときはほかをdisabledに。
  • ペットは複数選択可能
  • その他の動物はinputでstateで管理

という感じで、以下のようになりました。主要なstateやeffectだけ載せます。

  const [isPet, setIsPet] = useState<boolean>(true);
  const [isDog, setIsDog] = useState<boolean>(false);
  const [isCat, setIsCat] = useState<boolean>(false);
  const [isOthers, setIsOthers] = useState<boolean>(false);
  const [otherPet, setOtherPet] = useState<string>("");
  const classes = useStyles();
  useEffect(() => {
    if (!isDog && !isCat && !isOthers) {
      props.setIsPetText("ペットは飼っていません。\n\n");
    } else if (!isPet && isDog && !isCat && !isOthers) {
      props.setIsPetText("犬を飼っています。\n\n");
    } else if (!isPet && !isDog && isCat && !isOthers) {
      props.setIsPetText("猫を飼っています。\n\n");
    } else if (!isPet && !isDog && !isCat && isOthers) {
      props.setIsPetText(`${otherPet}を飼っています。\n\n`);
    } else if (!isPet && isDog && isCat && !isOthers) {
      props.setIsPetText("犬と猫を飼っています。\n\n");
    } else if (!isPet && isDog && !isCat && isOthers) {
      props.setIsPetText(`犬と${otherPet}を飼っています。\n\n`);
    } else if (!isPet && !isDog && isCat && isOthers) {
      props.setIsPetText(`猫と${otherPet}を飼っています。\n\n`);
    } else if (!isPet && isDog && isCat && isOthers) {
      props.setIsPetText(`犬と猫と${otherPet}を飼っています。\n\n`);
    }
  }, [isPet, isDog, isCat, otherPet, isOthers, props]);
  useEffect(() => {
    if (isPet === true) {
      setIsDog(false);
      setIsCat(false);
      setIsOthers(false);
    } else if (isDog === true || isCat === true || isOthers === true) {
      setIsPet(false);
    }

  }, [isPet, isDog, isCat, isOthers]);

  const otherPetHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();
    setOtherPet(e.target.value);
  };

超無理やりですが、できます。

chromeのconsoleをみる

また、chromeのdevtoolでconsoleから、時間が結構かかってるよ~という警告がたまに発生していたのですが、それはすべて解決できました。

例えば、

  • 文章生成するのは、ボタンを押したときだけにする
  • ↑最初はuseEffectで常に監視していたため関数が無駄に動作していたことが発覚した

他にも、

  • なるべく描画させる回数をへらすことを意識した。
  • ↑三項演算を多用していたときに時間がかかっていた。

などなど、他にも数え切れないですがたくさん学びがありました。

参考資料

https://material-ui.com/
https://ja.reactjs.org/

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

【基礎の基礎編】React + Express + GraphQLでHello World!

graphql-1200x630-960x504.png

はじめに

この記事は、GraphQLの基礎の基礎として、サーバーサイドとクライアントサイドを最低限実装し、GraphQLで接続する機能を実装しています。データの更新や、DB接続してデータを取得したりなどは行っていません。GraphQLでクライアントとサーバーの連携の概要を理解していただければと思います。

source

https://github.com/kyokozuka/react-node-express-graphql

GraphQLとは

GraphQLはAPIのための問い合わせ言語です。クエリを実行してデータを呼び出すためのランタイムをさすこともあります。

GraphQLの問い合わせ言語

GraphQLは、データベースに問い合わせるために開発されたSQLの考え方をインターネットに適用したものです。単一のGraphQLのクエリを使い、関連するデータをまとめて取得できます。また、データを変更したり削除したりすることもできます。SQLとGraphQLは問い合わせ言語という点で共通しています。

ただ、どちらも問い合わせ言語ではあるものの、GraphQLとSQLは全く異なるものです。

  • SQLのクエリはデータベースに対して実行される(データベースのための問い合わせ言語)
  • GraphQLのクエリはAPIに対して実行される(インターネットのための問い合わせ言語)

構文

  • SQL: SELECT, GraphQL: Query
  • SQL: INSERT/UPDATE/DELETE, GraphQL: Mutation

GraphQLはインターネットのための問い合わせ言語なので、ソケット通信を使ってデータの変更を監視するためのコマンドも用意されている。そのコマンドを"Subscription"と呼びます。

GraphQLサーバーの実装

まず、サーバーサイドとして、簡単な(Hello Worldを表示する)APIを作成してみましょう。
今回はApollo ServerはOSSで、非常にシンプルにセットアップできる上、本番環境に投入できる水準の機能を多く提供しています。
代表的な機能としては、サブスクリプションのサポート、ファイルのアップロード、既存のサービスのAPIからデータを取得する機能、クラウドサービスのApollo Engineがあります。

環境構築

ディレクトリ構成

myappServer/
  ├ node_modules
  ├ src
    ├ resolbers
      ├ index.js
      └ Query.js
    ├ index.js
    └ typeDefs.graphql
  └ package.json 
$ npm init -y // 初期化処理
$ npm install express apollo-server-express graphql-playground-middleware-express // モジュールのインストール

プログラム

index.js
const express = require("express");
const { ApolloServer } = require('apollo-server-express');
const { readFileSync } = require('fs');
const resolvers = require('./resolvers');

const typeDefs = readFileSync('./src/typeDefs.graphql', 'utf-8');

const app = express();

const server = new ApolloServer({typeDefs, resolvers});

server.applyMiddleware({ app })

app.get('/', (req, res) => res.end(`Welcome to the PhotoShare API`))

app.listen({ port: 4000 }, () => console.log(`GraphQL Server runnning @ http://localhost:4000{server.graphqlPath}`))
typeDefs.graphql
type Query {
    helloWorld: String!
}
resolvers/index.js
const Query = require('./Query');

const resolvers = {
    Query
}

module.exports = resolvers
resolvers/Query.js
module.exports = {
    helloWorld: () => 'Hello World'
}

サーバーの起動

$ cd myappServer
$ node src/index.js

>>>
GraphQL Server runnning @ http://localhost:4000{server.graphqlPath}

GraphQL Playgroundで実行してみましょう。
スクリーンショット 2021-01-14 171105.png

GraphQLクライアントの実装

次に、先ほど作成したGraphQLサーバーにクライアントからアクセスしてみましょう。

reactプロジェクトの作成

$ create-react-app myappClient

インストール

$ npm install graphql graphql-request
  • graphql-request

GraphQLオペレーションをAPIに送信するために利用できるフレームワークがいくつか存在します。その中で有名な"graphql-request"を使用します。これはfetchリクエストをPromiseでラップして、GraphQLサーバーへのリクエストを送信します。また、リクエストの構築とデータのパースに関する細かい処理を引き受けます。

プログラム

作成されたApp.jsを下記のように書き換えてください。

App.js
import { useState } from 'react';
import './App.css';
import { request } from 'graphql-request'

const url = 'http://localhost:4000/graphql'

const query = `
  query helloWorld {
    helloWorld
  }
`

function App() {
  const [hello, setHello] = useState('');

  const graphQL = async () => {
    const result = await request(url, query)
    setHello(result.helloWorld)
  }

  return (
    <div className="App">
      <button onClick={graphQL}>Click</button>
      <div>{hello}</div>
    </div>
  );
}

export default App;

request()でurlとqueryを引数として受け取ってサーバーへのリクエストを構築して、結果のデータを返します。ここでレスポンスされるデータは期待した通りのJSONデータ { "helloWorld": "Hello World" } がレスポンスされます。

サーバーの起動

$ cd myappClient
$ npm start

サーバーが起動したら、ブラウザが立ち上がり、「click」ボタンをクリックすると「Hello World!」と表示されるはずです。

スクリーンショット 2021-01-14 172910.png

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

React使う際の始動手順

この記事はZennに書いてましたが、難易度的にはQiita向けっぽく思えてきたため、こっちにもも同じもの投稿してちょっと反応見させてもらいます

やるときいつも「あーなんだっけ」となるので、後で見返したりうっかり忘れたりした時のためにまとめておきます

TL;DR

React使ってプロダクト作ったりする際の、サーバ起動あたりのコマンド軽くまとめ

前提条件(環境)

・Windows10
・npm

結論だけ

PowerShell開いて以下のコマンド

cd Reactプロダクト置いてるディレクトリ
npm start

これを実行することで、 http://localhost:3000/ (環境により違うかも)で作ったページが見れる

詳細な過程

Windows PowerShellを起動

コマンド叩いて実行する必要があるので、Windows環境ゆえWindows PowerShellを起動する必要があります
(Macだったらコマンドプロンプト、また使用するIDEによってはIDE上でできることも)
なので、検索欄なりスタートメニューなりからWindows PowerShellを起動します

対象のディレクトリに移動

起動コマンドについては、 create-react-app プロダクト名 で用意し、作っているReactプロダクトのあるディレクトリで起動する必要があります
そのため、まずcdコマンドで対象のディレクトリ( package.json のあるところ)に移動します

cd Reactプロダクト置いてるディレクトリ

環境によりますが、PowerShell開いたときのドライブ(たいていCドライブのはず)と別のドライブにディレクトリ置いてあるなら先にドライブ移動の必要もある(自分はDドライブに置いている)ので、その場合は以下の例のようなドライブ移動コマンドを先に打ってドライブを移る過程が必要です

D:

試してみたら、ドライブまたぐ場合であってもこれ無しにcdで普通に飛べました

コマンド実行制限の部分解除(場合による)

セキュリティのために、Set-ExecutionPolicyコマンドレットによってスクリプト実行の制限がなされていることがあります(「~ cannot be loaded because running scripts
is disabled on this system.」「このシステムではスクリプトの実行が無効になっているため、~を読み込むことができません」とか出てきたらこれ)
その場合は、以下のコマンド叩いてスクリプト実行の制限を、とりあえずローカルのファイルからは許可するように「RemoteSigned」に設定し、制限を部分解除すれば実行できるようになります

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process

やり終えたら

Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope Process

あたりで戻しておくと安全かもしれません
(状態に関しては Get-ExecutionPolicy コマンドで確認可能。今回の本題とは外れるので詳しく調べたい場合はググって)

サーバ起動

頻出なのでわざわざ言うことは無いと思う以下のコマンドで起動します

npm start

yarn使ってる場合はまた変わってきます( yarn run とか)

ページ確認

ここまでやれば http://localhost:3000/ (環境により違うかも)で、対象Reactプロダクトのページが標準ブラウザに設定しているブラウザで自動的に開きます
あとは確認しながらReactいじってください

素のHTMLなら特に前準備なくてもHTMLファイルをブラウザにドラッグするとかで見れるので、最初の頃はやや面倒に感じました(たまに今でも)
多分自動化する手段とかはありそう

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

ReactNとHooksによるグローバルな状態管理に関する調査と実験

この記事は実験なので内容の確からしさや有用性はなんともいえません

はじめに

Vueでグローバルステートを管理するライブラリと言えばVuexですが、ReactではReduxというのがあるらしいです。でもReact hooks登場以前の情報が多かったです。そしてパッと見、hooksがあれば、よりシンプルに状態を管理出来るのではないかと思い、色々調べて実験してみました。

ちなみに私はVueしか使ってこなかったReactの初学者です。初学者が王道を避けていきなり考察しているのは、アホっぽいですが、Hooks登場以後の王道パターンは調べてもわからなかった。まだ確立していないなら、どこかの誰かの役には立つかもしれない。

調べたこと

私が調べた時系列順です

Reduxについて

https://qiita.com/daijinload/items/c7015b1117c5beb7e49f

最初にこれを読んだためReduxやる気がなくなってしまったのですが、納得感のある記事(DBデータの二重管理はすべきでないとか)。ここでクラス+シングルトンというパターンに触れられていました。

シングルトンについて

https://qiita.com/NeGI1009/items/f8b17d856a4b15b1ecbc

JSでシングルトンとか考えた事なかったですが、上記の例に添えばとても簡単に実装出来ます。どこでimportしても同一のインスタンスが得られるので、まさにグローバルステートです。これを関心単位でクラス分けすれば、ライブラリを用いずとも見通しのよいステート管理が出来るのでは。

hooksについて

https://ja.reactjs.org/docs/hooks-reference.html#usecontext

useContextを用いれば、ツリーの子コンポーネントのスコープでグローバルな状態管理が出来るそう、ですがコードの見た目があんまり好きじゃなかったです。と思いきや、このAPIを用いたカスタムフックを提供するライブラリ「ReactN」があるらしい。

ReactNについて

https://qiita.com/tuttieee/items/e5b2725b3e58cae9ddd6
https://github.com/CharlesStover/reactn#function-components-1

ドキュメントのとおり、Function Componentに対応しuseGlobalというカスタムフックを提供しています。その前にreact-hooks-global-stateというライブラリもチェックしました、ReactNと似たような事が出来ますがこちらは"フラットな"Objectのみを管理可能でした(1階層のObjectという意味)。ReactNはクラスインスタンスを突っ込んでも問題ありません。

実験してみた

上記の調査から、

  • ReactNによるグローバルステート管理
  • データストアクラスのシングルトン

を試してみる

データストアクラス

/src/store/index.ts
export class DataStore {
    private val1: number;
    private val2: number;
    constructor() {
        this.val1 = 100;
        this.val2 = 1000;
    }
    add1(x: number) {
        this.val1 += x;
        return this;
    }
    add2(x: number) {
        this.val2 += x;
        return this;
    }
    get1() {
        return this.val1;
    }
    get2() {
        return this.val2;
    }
}
// インスタンスをexport
export const dataStore = new DataStore();

メンバ変数をprivateにする事でgetter,setterの使用を強制する

ReactNの環境構築

インストール

npm install reactn

手動で型定義

管理すべき状態(=State)の型を定義しておく

// /src/global.d.ts

import 'reactn';
import { DataStore } from './store';

declare module 'reactn/default' {
    export interface State {
        state1: DataStore;
        state2: number;
    }
}

関数コンポーネント側でimport

ReactNはReact自体をラップしますので、以下のようにimportします。

import React, { useEffect, useRef, useState } from 'react';

なら、以下に書き換える

import React, { useEffect, useRef, useState, useGlobal } from 'reactn';

ReactのAPIはそのまま使えて、useGlobalやsetGlobalといった関数が追加されています。

グローバル変数を定義・初期値をセット

setGlobal()を実行することでグローバル変数を定義出来ます。ドキュメントによれば、ReactDom.render()前にすべきとの事ですので、プロジェクトのエントリーポイントで一度だけsetGlobal()するのがよいでしょう。
https://github.com/CharlesStover/reactn#initializing-your-state

import { dataStore } from './store';
const state = {
    state1: dataStore,
    state2: 999,
};
setGlobal(state);

ReactDOM.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
    document.getElementById('root'),
);

グローバル変数を取得・更新

useGlobal()を用います。デフォルトのhooksであるuseState()と全く同じ使い方です。

// setGlobal()の引数のオブジェクトのkeyで取得する変数を指定出来る
// TypeScriptによるコード補完が効く
const [state1, setState1] = useGlobal('state1');
const [state2, setState2] = useGlobal('state2');

// 
return (
    <div
        className="App"
        onClick={() => {
            // DataStore
            //state1.val1 += 5000; //これはできない
            setState1(state1.add1(10));

            // number
            setState2(state2 + 1); // 999 -> 1000
        }}
    >
        <span>
            {state1.get1()} {state2}
        </span>
    </div>
);

シングルトンをsetGlobal()するなら、最低限getter,setterで値を操作するようにしないと、状態管理がエライ事になると思います。

グローバル変数の更新に応じDOMを再レンダリング

ReactNによるグローバル変数周りのDOM再レンダリングは以下の条件で発生します。

  • useGlobal()の2つ目の戻り値(例:setHogehoge())が実行された場合に再レンダリングが走る
  • その際に再レンダリングされるのは、useGlobal()を実行しているコンポーネントのみ

終わりに

  • 少なくとも、flatに値を保持するなら、ReactNで良い気がする
  • ReactNはflatなオブジェクトだけでなく、クラスやネストしたオブジェクトをグローバルで扱いつつDOMに反映させられるので便利だと思うけど、扱いが難しそう(useGlobalで取得したオブジェクトはconstだけど、メンバ変数は保護されないし…)

以下が解決すればうれしいなぁと妄想

  • setGlobal()した値が、下位のコンポーネントでしか参照出来ないようにする(今は親でも取得出来てしまうのでスコープを与えるということ)
  • DOMの再レンダリングのタイミング(今は(上記の例で言えば)setState()しないとレンダリングされないが、クラス変数に変更があったら、値を参照している箇所で再レンダリングが走って欲しい、もしかしたらuseEffectで出来たりして)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jsライブラリの学習を始めてから出てきた横文字いろいろ

DOM(Document Object Model)

HTMLの各タグ(要素)をNodeとみなして、階層構造で表現するモデルに変換することで、HTMLをJavaScript等で扱えるようにした技術。

仮想DOM

実際のDOMを構築する前に、バーチャルなDOMを構築するワンクッションを入れることで、プログラマがDOMを意識ないで済む。
さらに、バーチャルなDOMを構築する際に差分更新する仕組みをとることで画面更新を高速化できる。

この仮想DOMを取り入れたJavaScriptフレームワークが、REACTとかVue.js

バンドル

多くの React アプリケーションは、Webpackなどのツールを使ってファイルを「バンドル」している。バンドルはインポートされたファイルをたどって、それらを 1 つのファイルにまとめるプロセスです。このバンドルされたファイルを Web ページ内に置くことによって、アプリ全体を一度に読み込むことができます。

コーディングを行う際に、複数のファイルを1つのファイルに出力すること。コードをまとめるみたいな意味。

コード分割

必要なコードだけを読み込み、初期ロードの際に読む込むコード量を削減する。つまり高速。
例えば、トップページを呼び出すと、トップページに必要なjsだけが読み込まれる。
トップページに必要なコードが読み込まれた後に、Linkコンポーネントによってリンク先のページのコードをバックグラウンドでプリフェッチングする。
リンクを押す頃には読み込みが終わっているため、ページ遷移も高速。すごい。。。

プリフェッチング

コードを先に読み込んでおくこと。
preがあらかじめみたいな意味。

コンポーネント

何かの部品。
Reactでは、見た目を機能ごとにコンポーネント化して、コンポーネントを組み合わせることでWebサイトの見た目を作る。

CSS-in-JS

JSの中でCSSを書ける。
styled-jsxは、CSS-in-JSのライブラリ。
1ファイルで書ける。
react.componentとして提供されてる分、楽。
例)styled-components、 emotion、 MATERIAL-UI

CSS Modules

CSS-in-JSとは別に、CSSを生で書くこともできる。
CSSファイルをReactコンポーネントの中でインポートすることができる。
CSS Modulesは自動的に一意のクラス名を生成するため、クラス名の衝突が起こらない。(CSS-in-JSも)
Next.jsのコード分割は、CSSモジュールに対しても機能する。
例)テイルウィンド、 
チュートリアルした感じ、CSS Modulesの方が地道だけど分かりやすそう。
どっちを使うにしろ、アプリごとでどっちかに統一した方が良さそう。
Tailwindは、CSSModulesではなくグローバルに読み込んでpurgecss (使われてない CSS を削除するものです)と合わせて使うのが基本。なのでパフォーマンスいい。

プリレンダリング

レンダリング:何らかのデータをもとにして表示内容を作る。HTMLをもとにページを生成する。みたいな

Next.js はすべてのページをHTMLでプリレンダリングする。DOMにするということ??
まず各ページのHTMLを生成する。その後、そのページに必要な最小限のJSを関連付ける。(このプロセスはハイドレーションと呼ばれる)。

ビルド

ソースコードを実際に動くプログラムに変換するコンピュータ上で実行されるプロセス。

静的生成(SSG)

ISR:SSGを何秒間隔でするみたいな機能。

サーバサイドレンダリング(SSR)

リアルタイム性が求められるアプリはこっちが向いてる。

クライアントサイドレンダリング(CSR)

プリレンダリングは行わずに、クライアント側の JavaScript を使用してデータを埋め込むこと。?

フェッチ

データベースなど外部データの取得

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

Netlifyへ新規Gatsbyサイトをデプロイする

最近Gatsbyをさわることがあって他にもいろいろ試したいので、"新規サイトをローカルで生成してNetlifyへのデプロイ"までを忘れないようにメモしました。

NetlifyとGatsby

Netlify

Netlifyは静的サイトのホスティングサービスです。

  • 無料で使い始めることができる
  • 結びついているgitリポジトリにpushすると自動で最新バージョンのサイトをビルドできる

という特徴があり、プロトタイピングに便利です。

Gatsby.js

静的サイトジェネレータの1つ。こちらがとても詳しいです。

環境など

  • macOS Catalina
  • Node.js v12.18.3
  • GitHubでリポジトリを作成してローカルからpushできる状態にある

ローカル開発用にセットアップ

バージョン2.27.0で搭載されたインストール方法を使ってみます。

npm init gatsby

いくつかの質問が続くので答えていきます。

#名前を聞かれる デフォルト名"My Gatsby Site"でよければそのままEnter
What would you like to call your site?
✔ · My Gatsby Site
#以下の3つの質問はそのままEnterで進む
What would you like to name the folder where your site will be created?
✔ node/ my-gatsby-site
✔ Will you be using a CMS?
· No (or I'll add it later)
✔ Would you like to install a styling system?
· No (or I'll add it later)


#この質問は複数選択 なにも選択しないので↓キーでDoneまで進めてEnter
? Would you like to install additional features with other plugins?
(Multiple choice) Use arrow keys to move, enter to select, and choose "Done" to confirm your choices
 ◯ Add the Google Analytics tracking script
 ◯ Add responsive images
 ◯ Add page meta tags with React Helmet
 ◯ Add an automatic sitemap
 ◯ Enable offline functionality
 ◯ Generate a manifest file
 ◯ Add Markdown support (without MDX)
 ◯ Add Markdown and MDX support
  ─────
❯ Done


Thanks! Here's what we'll now do:

    🛠  Create a new Gatsby site in the folder my-gatsby-site

? Shall we do this? (Y/n) › Yes
#最後にこのプロンプトで止まるのでそのままEnter

うまくいくと以下のメッセージがでます。

🎉  Your new Gatsby site My Gatsby Site has been successfully created

サイト確認(ローカル)

npmも使えますが今回はyarnを使ってます。

開発モードで確認

#上記設定で作成されたディレクトリへ入る (上記デフォルトのままだとmy-gatsby-site)
cd my-gatsby-site
#開発モードでサイトのサーバ起動
yarn develop

メッセージが多く出ると思いますが、通常

You can now view my-gatsby-site in the browser.
⠀
  http://localhost:8000/

が含まれていると思います。http://localhost:8000/へアクセスすると現状のサイトを確認できます。

image.png

サーバの終了はcontrol+Cです。

本番用サイトを生成して確認

#本番用サイトを生成
yarn build
#生成された本番サイトをローカルサーバで起動
yarn serve

開発内容によってはビルド段階でエラーがでることがあるので、デプロイ前に上記コマンドで一度ローカル確認したほうがいいです。
問題なければ以下の出力がでてアクセスできるようになります。

You can now view my-gatsby-site in the browser.
⠀
  http://localhost:9000/

これもサーバの終了はcontrol+Cです。

デプロイ

Githubにプッシュ

githubに新規リポジトリを作ったあと、ローカルからプッシュします。

Netlify

https://www.netlify.com/

アカウントがない場合は sign up からアカウント作成できます。自分はGitHubアカウント認証で作りました。

ログイン直後のホーム画面にある New site from Git をクリック
image.png

Create a new site の画面になるのでGitHubのボタンをクリック
image.png

GitHubリポジトリの詳細設定に移ります。先程作成したリポジトリが所属するGitHubアカウントをクリック。
image.png

GitHubへのnetlifyプラグインのインストール設定になります。Only select repositoriesを選択 -> select repositories で先程作成したリポジトリを選択。netlifyがGitHubへ接続する際のアクセス設定について説明されているので、これで問題なければInstallをクリック。
image.png

もう一度Create a new site の画面になるのでGitHubのボタンをクリック。
先程追加したリポジトリが追加されているはずなのでそれをクリック。
image.png

デプロイ設定に移るが、Netlify側で自動設定しているようで通常は下記のようになる。そのまま Deploy Site をクリック。
image.png

Site deploy in progressはデプロイ進行中。デプロイが終わると表示がURLに変わってアクセス可能になります。
image.png
image.png

参考文献

https://qiita.com/umamichi/items/9bd08a21fddc71588efc

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

【TypeScript + React Hooks】useReducerでactionの型定義をtypeの値によって変更する

はじめに

TypeScript + React HooksのuseReducerで、オブジェクトの配列のstateを作成したとき、
dispatch関数がtypeの値に応じて必要な引数だけを要求するようにしたくなった

つまり、reducerの引数であるactionが、
typeの値に対応したpayloadを持つように型定義したい

方法

型定義において、typeをtype: string;ではなく具体値で定義する

interface AddAction {
  type: 'add',
  payload: {
    id: number;
    a: number;
    b: string;
  }
}
interface RemoveAction {
  type: 'remove',
  payload: {
    id: number;
  }
}

定義したそれぞれの型を | でつなぐ

const reducer = (
  state: Array<MyDataType>,
  action: AddAction | RemoveAction,
) => {
  if (action.type === 'add') {
    // addの処理
  } else if (action.type === 'remove') {
    // removeの処理
  }
}

実装例

  • オブジェクトの配列であるstateについての実装例
  • users:ユーザ情報(id, name, age)を管理するオブジェクトの配列のstate
  • 新たにユーザ情報を追加するaddと、idをもとにageを更新するupdateAgeを実装
sample.tsx
import React, { useReducer } from 'react';

interface User {
  id: number;
  name: string;
  age: number;
  gender: string;
}

interface AddAction {
  type: 'add';
  payload: User;
}

interface UpdateAgeAction {
  type: 'updateAge';
  payload: {
    id: number;
    age: number;
  };
}

export const reducer = (
  state: Array<User>, 
  action: AddAction | UpdateAgeAction
): Array<User> => {
  if (action.type === 'add') {
    // addの処理
  } else if (action.type === 'updateAge') {
    // ageの更新処理
  }
}


const MyComponent: React.FC = () => {
  const [users, usersDispatch] = useReducer(reducer, []);

  const addUser = (newUser: User) => {
    usersDispatch({
      type: 'add',
      payload: newUser,
    }
  }
  const updateAge = (userId: number, newAge: number) => {
    usersDispatch({
      type: 'updateAge',
      payload: {
        id: userId,
        age: newAge,
      }
    }
  }
  // 以下省略
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む