20200315のReactに関する記事は14件です。

apollo Clientのcacheに有効期限をつける

はじめに

Apollo Client ver3.0では新たにcacheについていくつか機能が追加されています。その一つがcacheの削除です。
cacheに有効期限を設ける方法について、考えたので備忘録として残します(もっといい方法があれば教えてください)

evictとgc

今回は Apollo Client ver3.0で新しく追加されたgcとevictを使います。詳しい説明は公式docを参照してください

evict

evictはcacheに保存されているdataを消すことができます。
使う際は__typenameとidをセットにして使います

cache.evict('__typename:id')

gc

gcはevictで特定のdataを消した後cacheに残っているゴミを消すことができます。
ここでいう今回消したdataをrefとして持つ親データです。子を消した事によって、親が参照するdataがなくなりますが、gcを使う事によってこういったゴミを消すことができ、cacheのdataがクリーンに保たれます。

cache.gc()

実装例

以前挙げたQiitaの記事のQueryで保存されたcacheに有効期限10分をを設定したコードです。(apollo clientの中に有効期限設定できる機能があるのかも?)

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";

const link = new HttpLink({
  uri: "http://localhost:4000/graphql"
});

const cache = new InMemoryCache()

const cacheInterval = setInterval(() => {
  cache.evict('Comment:1');
  cache.gc();
}, 10 * 60 * 1000);

const client = new ApolloClient({
  link,
  cache,
  defaultOptions: {
    watchQuery: {
      fetchPolicy: "cache-first"
    }
  }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

apollo clientのcacheに有効期限をつける

はじめに

Apollo Client ver3.0では新たにcacheについていくつか機能が追加されています。その一つがcacheの削除です。
cacheに有効期限を設ける方法について、考えたので備忘録として残します(もっといい方法があれば教えてください)

evictとgc

今回は Apollo Client ver3.0で新しく追加されたgcとevictを使います。詳しい説明は公式docを参照してください

evict

evictはcacheに保存されているdataを消すことができます。
使う際は__typenameとidをセットにして使います

cache.evict('__typename:id')

gc

gcはevictで特定のdataを消した後cacheに残っているゴミを消すことができます。
ここでいう今回消したdataをrefとして持つ親データです。子を消した事によって、親が参照するdataがなくなりますが、gcを使う事によってこういったゴミを消すことができ、cacheのdataがクリーンに保たれます。

cache.gc()

実装例

以前挙げたQiitaの記事のQueryで保存されたcacheに有効期限10分をを設定したコードです。(apollo clientの中に有効期限設定できる機能があるのかも?)

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";

const link = new HttpLink({
  uri: "http://localhost:4000/graphql"
});

const cache = new InMemoryCache()

const cacheInterval = setInterval(() => {
  cache.evict('Comment:1');
  cache.gc();
}, 10 * 60 * 1000);

const client = new ApolloClient({
  link,
  cache,
  defaultOptions: {
    watchQuery: {
      fetchPolicy: "cache-first"
    }
  }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GatsbyとGitHub PagesとGitHub Actionsで技術系イベントサイトを無料・爆速で作る方法

技術系イベントサイト用に以下を作成しました。

uedayou/event-page-gatsby

このサイトは以下のことができます。

コミュニティベースでハッカソンやアイデアソンなど技術系イベントを開催するときに、結構イベントサイト作るのに費用や時間がかけられないことがあると思います。これを使えば、無料でかつ爆速でサイト立ち上げられます。その方法を以下で説明します。

どのようなサイトができるかは、一番最後の公開事例を見てください。

準備

まず、uedayou/event-page-gatsbyを自分のGitHubアカウントにフォークして、自分のアカウント内のリポジトリを clone してください。

$ git clone https://github.com/[あなたのアカウント名]/event-page-gatsby.git

event-page-gatsbyディレクトリに移動して、npm install を実行します。

$ cd event-page-gatsby
$ npm install

インストールが終わったら、npm run developを実行して、サイトが生成されるか確認します。

$ npm run develop

localhost:8000をブラウザでアクセスして、Webページが表示されれば成功です。

http://localhost:8000/

npm run developが実行中であれば、編集された内容がすぐにブラウザに反映されます。

編集

このサイトにはヘッダー部、イベント告知部、ブログ部、フッター部の4つから構成されています。それぞれの編集部分について説明します。デモサイトを見ながら、読むとわかりやすいと思います。

デモサイト

ヘッダー部

ヘッダー部

ヘッダー部については、gatsby-config.js を編集します。

gatsby-config.js
module.exports = {
  pathPrefix: "/event-page-gatsby",
  siteMetadata: {
    siteTitle: `技術系イベントページ`,
    siteTitleAlt: `技術系イベントページ`,
    siteHeadline: `技術系イベントページ`,
    // Will be used to generate absolute URLs for og:image etc.
    siteUrl: `https://uedayou.github.io/event-page-gatsby/`,
    // Used for SEO
    siteDescription: `技術系イベントで便利に利用できるGatsbyで作られたサイトです。`,
    // Will be set on the <html /> tag
    siteLanguage: `ja`,
    // Used for og:image and must be placed inside the `static` folder
    siteImage: `/banner.jpg`,
    // Twitter Handle
    author: `@uedayou`,
    // ここでサイトのライセンスを規定できます。たとえば、No rights reserved など
    siteLicense: `All rights reserved`,
    // ここでブログ部分のタイトルを指定できます。
    blogLabel: `ブログ`,
    // ここでタグ部分のタイトルを指定できます。
    tagsLabel: `タグ`,
  },
...

siteTitle,siteTitleAlt, siteHeadline, siteUrl, siteDescriptionなど適宜書き換えてください。
もし、サイトコンテンツのライセンスをオープンデータにしたい場合は、siteLicenseを書き換えてください。我々のサイトでは、No rights reserved(パブリックドメイン)としています。
siteImageで指定する画像などサイト内で利用したいスタティックファイル(サイトのアイコン等)は、staticディレクトリにコピーすればアクセスできます。

pathPrefixは、サイト内での絶対パスを指定してください。例えば、https://uedayou.github.io/event-page-gatsby/の場合、/event-page-gatsby を指定します。独自ドメインを使用する(例えば、https://iodd2020osaka.lodosaka.jp/)であれば、/を指定してください。この設定が間違っていると、サイト内リンクがすべてリンク切れになりますので、注意してください。

右側の3つのリンク部分は以下を編集すると変更されます。

gatsby-config.js
...
        externalLinks: [
          {
            name: `GitHub`,
            url: `https://github.com/uedayou/`,
          },
          {
            name: `このリポジトリ`,
            url: `https://github.com/uedayou/event-page-gatsby`,
          },
          {
            name: `個人ページ`,
            url: `https://uedayou.net/`,
          },
        ],
      },
...

イベント告知部

イベント告知部

イベントの告知は、src/@lekoarts/gatsby-theme-minimal-blog/texts/hero.mdxファイルを編集します。

hero.mdx
技術系イベントの告知部分として使えます。

#### この部分を編集するには

「src/@lekoarts/gatsby-theme-minimal-blog/texts/hero.mdx」  

上記ファイルを編集してください。  
Markdownにより編集できます。

#### 実際に使用しているページ

[インターナショナル・オープンデータ・デイ 2020 大阪](https://iodd2020osaka.lodosaka.jp/)  
サンプルとして参照してください。

Markdownで記述できます。

ブログ部

ブログ部

本サイトには、ブログ機能があります。我々のイベントでは、情報提供を集約するサイトとして使用していますので、ブログ部分に提供された情報をまとめることとしました。
例えば、ハッカソンやアイデアソンでは、各チームの成果をチーム自身でブログ部分にまとめてもらい、プルリクエストを募れば、成果をまとめることをチームに一任することができるでしょう。

ブログは、content/posts/以下に追加していきます。
まず、記事単位でディレクトリを作ります。数字から始まるディレクトリは受け付けてくれないようなので、半角英字から始まるようにしましょう。そのディレクトリの中にindex.mdxファイルを作ります。index.mdxファイルを以下のようなフォーマットで記述すれば、ブログ記事が追加されます。

index.mdx
---
title: "ここにタイトルを指定します"
date: 2020-03-14
tags:
  - サンプル
  - テスト ページ
slug: "/path-of-this-post"
---

### ブログ部分の使い方

上記の、title, date, slug を指定してください。 
tags はなくてもかまいません。slug がないと、title がパス名となります 

ブログリストのタイトルは、gatsby-config.js の  
「siteMetadata.blogLabel」  
により変更できます。  

title, date, slug(ブログ記事のパス)が指定されていれば後は、記事をMarkdownで書くだけです。

index.mdx

フッター部

フッター部

この部分は、src/@lekoarts/gatsby-theme-minimal-blog/texts/bottom.mdxを編集します。
我々のイベントでは、関連するリンク集として使いました。

bottom.mdx
<Title text="リンク" />

- [個人ページ](https://uedayou.net/)
- [GitHub](https://github.com/uedayou)
- [このリポジトリ](https://github.com/uedayou/event-page-gatsby)

<Title text="リンク" />リンクを変えれば、セクション名も変わります。ここは、それぞれ自由に変更して、用途に合わせて使ったほうがいいと思います。

まだ、細かいところはありますが(随時追加したいと思います)、上記を編集すれば、最低限体裁の整ったサイトはすぐ作れます。サイト作成に時間がかけられない、という場合、結構あると思いますが、是非使ってください。技術系や、イベントのみならず、すべてがシンプルなので、いろいろ使い道はあると思います。

サイト公開

ここまでの編集が終わったら、サイト公開の準備をしましょう。編集内容をgit pushする前にまず、GitHubリポジトリのSettingsページでサイトをGitHub Actionsを利用して自動公開できるように設定しましょう。この設定は一度行えば、以後設定する必要はありません。

Settings

秘密鍵・公開鍵作成

このサイトを自動的に公開するためには、秘密鍵と公開鍵が必要になります。
まず、鍵を作成します。

$ ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""

※ Linux, Macのみ

ここで、秘密鍵(gh-pages)と公開鍵(gh-pages.pub)の各ファイルが作成されます。
そのほかの方法は、こちらを参照してください。
GitHub Actionsを利用したGitHub Pagesのデプロイは以下の記事の方法を利用しています。

GitHub Actions による GitHub Pages への自動デプロイ

公開鍵を登録

Settings > Deploy Keys ページを開き、公開鍵を追加(Add deploy key)します。

Titleに任意の公開鍵の名前、Keyに先ほど作成したgh-pages.pubをテキストエディタで開き、文字列をすべてコピーして、貼り付けます。
Allow write accessにチェックを入れて、Add keyボタンを押してください。

公開鍵を登録

秘密鍵を登録

秘密鍵はSettings > Secretsで設定します。

TitleACTIONS_DEPLOY_KEYと入力してください。
(※この名前以外では自動デプロイされません)
Valueに公開鍵と同じく、gh-pagesをテキストエディタで開き、文字列をすべてコピーして貼り付けてください。
Add secretボタンを押せば完了です。

秘密鍵を登録

サイト更新作業

公開鍵、秘密鍵の設定が終われば、git push origin master で更新内容をpushしましょう。
masterブランチにpushされれば、GitHub Actionsが自動的にGitHub Pagesの作成・更新を行います。

GitHub Actions のページでその処理の流れを確認できます。
以下のようになれば、更新は完了です。

https://[あなたのアカウント名].github.io/event-page-gatsby/

を開いてみてください。ページが表示されるはずです。

自動デプロイ

その他

独自ドメインでの運用は、通常と同じ方法で可能です。CNAMEファイルはstaticディレクトリに入れてpushすれば反映されます。

GitHub Pages サイトのカスタムドメインを管理する

公開事例

uedayou/event-page-gatsbyは、2020年3月7日~3月17日まで開催されるインターナショナル・オープンデータ・デイ 2020 大阪のイベントサイトとして作成したものです。

https://iodd2020osaka.lodosaka.jp/の表示例:
イベントでの使用例

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

Material-UIでコンポーネントの色を変更する方法まとめ[React+Typescript]

はじめに

フロントまだまだよくわからないけれど、React+Typescript+Material-uiを使って、かっこいいWebアプリを作ろうとしてつまずいている初心者がMaterial-UIでコンポーネントの色を変えようとして四苦八苦したので、そのまとめです。
公式のドキュメントやサンプルコードを参考に色を変える方法として以下の3つでまとめました。

  • Material-uiのPaletteを使う方法
    • Paletteの色を変えたい
  • style属性を使う方法
  • className属性を使う方法

今回は以下のSimple App Barの色を変更していきます。
公式のサンプルコードはこちらです。

image.png

変更していくコードの概要

今回は説明の都合上、以下のコマンドを実行して、そのままApp.tsxを変更していきます。

npx create-react-app learn-material-ui --typescript
// material-uiのパッケージのインストール
yarn add @material-ui/core
yarn add @material-ui/icons

バージョンなどは以下です。

node.js:v10.16.0
"@material-ui/core": "^4.9.5",
"@material-ui/icons": "^4.9.1"

App.tsxを公式のサンプルコードを参考に以下のように書き換えます。
これで実行すると上で紹介したスクリーンショットのようになります。

import React from "react";
import "./App.css";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import IconButton from "@material-ui/core/IconButton";
import MenuIcon from "@material-ui/icons/Menu";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      color: "#388e3c",
      backgroundColor: "#81c784"
    },
    menuButton: {
      marginRight: theme.spacing(2)
    },
    title: {
      flexGrow: 1,
      textAlign: "center"
    }
  })
);

function App() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <IconButton
            edge="start"
            className={classes.menuButton}
            color="inherit"
            aria-label="menu"
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" className={classes.title}>
            News
          </Typography>
          <Button color="inherit">Login</Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}

export default App;

Material-uiのPaletteを使う方法

一番単純なAppBarcolorを利用する方法を紹介します。
コードの変更箇所は単純で以下のようにAppBarタグにcolorを追加するだけです。

<AppBar position="static" color="secondary">

公式のドキュメントによると、AppBarcolorは以下の要素に対応しているそうです。

  • default
  • inherit
  • primary
  • secondary
  • transparent

「いやいや、この要素どれがなんなのかさっぱりわからないよ……」となったので、自分なりに調べてみたり実行したりした結果が以下です。
ちなみにブラウザの検証のElementsAppBarの中を見ると、cssの中身が分かります。

  • default:モノクロ
    image.png

  • inherit:親要素のcolorinherit(サンプルコードでは親要素のdivに設定していたcolor: "#388e3c"が継承されました)
    image.png

  • primary:デフォルトでしていされている。material-uiが用意しているDefault Themepaletteprimary
    image.png

  • secondary:material-uiが用意しているDefault Themepalettesecondary

  • transparent:colorinheritで、background-colortransparent(透過)
    image.png

このなかのprimarysecondaryはmaterial-uiで色に統一感を持たせるためのPaletteというものの1つです。
様々なコンポーネントで利用されていて、コンポーネントごとに利用できるものが異なります。
デフォルトでは以下のような色が設定されています。
image.png

この色を変更する方法を次に紹介します。

Paletteの色を変えたい

アプリのイメージカラーで統一したいといったときにcreateMuiThemeThemeProviderを利用します。
公式ページを参考にしつつ、コードを以下のように変更してみました。

import React from "react";
import "./App.css";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import IconButton from "@material-ui/core/IconButton";
import MenuIcon from "@material-ui/icons/Menu";
import { createMuiTheme } from "@material-ui/core/styles";
import { teal } from "@material-ui/core/colors";
import { ThemeProvider } from "@material-ui/styles";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      color: "#388e3c",
      backgroundColor: "#81c784"
    },
    menuButton: {
      marginRight: theme.spacing(2)
    },
    title: {
      flexGrow: 1,
      textAlign: "center"
    }
  })
);

// 追記
const theme = createMuiTheme({
  palette: {
    primary: {
      main: teal[500]
    },
    secondary: {
      main: "#00bcd4"
    }
  }
});

function App() {
  const classes = useStyles();
  return (
    <ThemeProvider theme={theme}> // 追記
      <div className={classes.root}>
        <AppBar position="static" color="primary">
          <Toolbar>
            <IconButton
              edge="start"
              className={classes.menuButton}
              color="inherit"
              aria-label="menu"
            >
              <MenuIcon />
            </IconButton>
            <Typography variant="h6" className={classes.title}>
              News
            </Typography>
            <Button color="inherit">Login</Button>
          </Toolbar>
        </AppBar>
      </div>
      <div className={classes.root}>
        <AppBar position="static" color="secondary">
          <Toolbar>
            <IconButton
              edge="start"
              className={classes.menuButton}
              color="inherit"
              aria-label="menu"
            >
              <MenuIcon />
            </IconButton>
            <Typography variant="h6" className={classes.title}>
              News
            </Typography>
            <Button color="inherit">Login</Button>
          </Toolbar>
        </AppBar>
      </div>
    </ThemeProvider> // 追記
  );
}

export default App;

すると以下のように色を変更したAppBarを2つ表示することができました。
image.png

ちなみに@material-ui/core/colorsで、Materialデザインの色を利用することができます。
色の一覧はこのページを参照してみてください。

style属性を使う方法

以下のようにAppBarタグを変更します。

<AppBar position="static" style={{ color: "#e0f2f1", backgroundColor: "#004d40" }}>

すると、以下のように色を変更することができます。

image.png

className属性を使う方法

まず、以下のようにuseStylesを書き換えます。

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      color: "#388e3c",
      backgroundColor: "#81c784"
    },
    bar: {
      color: "#e0f2f1",
      backgroundColor: "#004d40"
    },
    menuButton: {
      marginRight: theme.spacing(2)
    },
    title: {
      flexGrow: 1,
      textAlign: "center"
    }
  })
);

次にAppBarタグを以下のように変更します

<AppBar position="static" className={classes.bar}>

すると、以下のようにstyle属性を使う方法と同じく書き換えることができます。

image.png

おわりに

Material-UIでコンポーネントの色を変える方法をまとめました。
Material-UI、とても便利なので使いこなしていきたいですね…!
また、わかったことなどありましたら、まとめていきたいと思います。

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

Reactのフックの使い方①

初心者のためのReact Hooks

なるたけ分かりやすく解説してみます。

話す内容

以下の流れで、話していきます。
1:フックとは?
2:簡単な使い方(useState)
3:API連携する際の使い方(useState)(Reactのフックの使い方②で解説します。)

3までいけば、実用的な使い方を少し習得できるかと思います。

フックとは?

そもそもフックの導入は、React16.8以降からの機能で、stateなどの機能をclass型ではなく、function型で書けるようになったというのが始まりです。今まで、componentDidMount, componentDidUpdate, componentWillUnmountと書きまくって、ライフサイクルメソッドを通していたものが、フック使っちゃえばそれら無くてもOKや!となるのです。結果として、コードがより短くなります。

使う際は、
import React, { useState } from 'react';
などと書いて、使うことができます。

フックの種類は、カスタムフック(自分で作れちゃうオリジナルのフックのこと)をのぞいて、10種類あります。全て、必ずuseXXXから始まります。カスタムフックもuseXXXとするのが一般的です。

基本のフック
・useState
・useEffect
・useContext

追加のフック
・useReducer
・useCallback
・useMemo
・useRef
・useImperativeHandle
・useLayoutEffect
・useDebugValue

その中でも、基本のフックが上3つ(useState, useEffect, useContext)なので、これは最初に使えるようにしましょう。ただ、今回の記事ではuseStateに絞って解説していきます。一つ分かれば、後はスラスライケるかと思います。

簡単な使い方

説明だけ見てても、意味ないので早速使っていきましょう。
使うのは、codesandboxにすることとします。

https://codesandbox.io/s/wizardly-borg-jt7j4

すでに、上のリンクには、ReactとuseStateの記述がされているので、即時にフックが使えます。

実際に、以下からフックスの説明に入っていきます。

useStateの導入

まず、1行目ですが、{ useState }となっております。これは、useXXXが、React側で宣言されている関数であるため、その関数を使うことを表記するために{}を使って、導入しています。

押すとカウントが増えるボタンを作る

今回作るのは、簡単なカウント型のボタンの作成です。たった13行ですので、ささっと見ていきましょう。

import React,{useState} from "react";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
    </div>
  );
}

今、このようなfunction型の表記があります。
まず、必要なのは、今回使うuseStateに現在の state の値と、それを更新するための関数とをペアにして返すことです。

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
    </div>
  );
}

const ~ の部分を追加しました。ここでは、state変数を宣言しています。第一引数のcountは、その時々での状態を保持してくれるものです。第二引数のsetCountは、更新してくれるための関数です。
useState(0)の部分は、stateの初期値です。ざっと書いていますが、countは常に表示されている、setCountは常に状態を変更してくれると考えてください。この2つは常にペアです。

次に、中身の部分を作っていきます。

<div className="App">
  <p>You clicked {count}times</p>
  <button onClick={()=>setCount(count + 1)}>click me</button>
</div>

{count}の部分は、毎回button内にあるonClickメソッドが押されるたびに1ずつ増えていきます。今回、buttonが押されると、表示が変わる部分は、この{count}部分のみです。

onClick={()=>setCount(count + 1)}

ここの意味としては、クリックされたらsetCount内にあるものを実行するよということです。
setCount内には、(count + 1)とあります。

今回、
const [count, setCount] = useState(0);

のように、useState(0)、つまり初期値を0としているため、0 + 1となり2が表示されます。これがもし、useState(5)となっていれば、初期値5 + 1となり6を返します。

完成したのは以下のようなモノです。
https://codesandbox.io/s/infallible-chatterjee-lb91e

まとめ

ReactのuseStateについて説明してみました。
次回は、Reactのフックの使い方②で、簡単なAPI実装を含めたフックの使い方を解説したいと思います。

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

グローバルなステートをめっちゃシンプルに扱えるReactN

Reactでグローバルなステートを扱う場合、

Reduxを使用するのが主流かと思いますが、
ReactN
というライブラリによって、グローバルなステートをめっちゃシンプルに扱えたので、めっちゃシンプルに紹介します。

「めっちゃシンプルに扱えた」という表現をもう少し具体的な表現にすると、
「ちょっとだけ初期設定をしたら、useStateを使うノリでグローバルなステートを使えた」
という感じです。

ReactNを使用したサンプル

サンプル(CodeSandBox)
9ogy2-2vsh7.gif
各ページで単一のstateとsetStateを使用しています。
上記のサンプルはTypeScriptと、ページ遷移にreact-router-domを使用しています。

ReactNの使い方

インストール

npm i reactn

または、

yarn add reactn

ちょっとだけ初期設定

  • index.tsxで、グローバルに使いたいstateの名前と初期値を、下記のように設定します。
import React, { setGlobal } from "reactn";
// 'from "reactn"'で、Reactの諸機能とReactNの諸機能をまとめてインポートできます。

setGlobal({ count: 0 }); // setGlobal({ stateの名前: 初期値 })

ReactNのGitHubのREADME.mdに、「ReactDOMのレンダー前にsetGlobalをするのがオススメ」と書かれているので、サンプルではそれに従っています。

  • global.d.tsで、グローバルステートの型を下記のように定義します。
import "reactn";

declare module "reactn/default" {
  export interface State {
    count: number;
  }
}

初期設定は以上です。
あとはuseStateを使うノリで、グローバルステートを扱えます。

グローバルステートを扱う

グローバルステートを使いたいコンポーネントで下記のように記述すると、グローバルなステートを扱えます。

import { useGlobal } from "reactn";

const [count, setCount] = useGlobal("count");
// useGlobal("setGlobalで設定したstateの名前")

上記の通りグローバルステートの扱い方が、const [count, setCount] = useState(0);のような扱い方とほぼ同じではないでしょうか。

また、

const count = useGlobal("count")[0]; // stateだけを使う
const setCount = useGlobal("count")[1]; // setStateだけを使う

上記のようにグローバルステートを扱うこともできます。

以上がReactNを利用したグローバルステートの扱い方です。

ReactNの"N"の正体

「ReactNの"N"はなんだろな」と思ったので、ReactNのREADMEやIssuesやReactNの記事を読み漁ったのですが、答えを見つけることができませんでした。

なので、ReactNの作者であるCharles Stoverさんに「"N"ってなんですか」と伺ったところ、下記のニュアンスの返答をいただきました。

ReactNは「より多くの機能を備えたReact」にしたかった。
そこで「Reactに足りない部分までカバーする」→「"React"に足りない部分"ion"(Reactの由来はReaction)までカバーする」という命名コンセプトを思いつき、"Reacti"や"Reactio"という名前にしようとしたが、いずれも既に使われていた。
それで"ReactN"という名前にいきついた。
だから"N"は何かの略ではない。

平たくしてまとめると、

「"Reaction"に対してはReactは"React"までだから、オレのライブラリはその先の"ion"まで届いてみせるぜ!」

ということかと思います。

些細な質問に答えてくださったことと、ReactNという素敵なライブラリを制作していただいたCharles Stoverさんに、謝意を申し上げます。

参考記事・関連リンク

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

Reactのエレメントについてかなりザックリ解説する

Reactのエレメントとは

Reactはコンポーネントとという小さいパーツを複数組み合わせることでUIを構築しています。またコンポーネントは直接使用するのではなく、インスタンス化(設計図であるクラスを元にオブジェクトであるモノを生成すること)することで使用します。このインスタンス化によって生まれたオブジェクトをエレメントと呼びます。

またこの記事ではコンポーネント、エレメント、バーチャルDOMについて非常にわかりやすく解説がされています。

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

Next.jsをGCP Cloud Run上で実行する方法

Next.jsのチュートリアルをGCP Cloud Run上で動かすための方法をメモ。

概要

  1. Next.JSの6 fetching dataチュートリアルをやる
  2. Docker 化する
  3. Cloud Container Registryにpushする
  4. Cloud Runにdeployする
  5. アクセスして動ていることを確認

next.jsのtutorialをcloneする

git clone https://github.com/zeit/next-learn-demo.git

Tutorialをやる

今回はserverless設定を試したかったので、6-fetching-dataを使います。

cd next-learn-demo/6-fetching-data

Tutorial自体は下記を見ながらやってください。
https://nextjs.org/learn/basics/fetching-data-for-pages

詳細の説明は省きますが、映画情報APIから情報をとってきて成形するチュートリアルになってます。

Docker化する

手元の環境に合わせてnode12でDokcerコンテナを作ります。

Dockerfile
FROM node:12.16.1-alpine

WORKDIR /app

ENV PATH /app/node_modules/.bin:$PATH

COPY . .
RUN npm install && npm run build

# start app
CMD [ "npm", "run", "start" ]

Cloud Run用に公開するポートを指定する

Cloud RunはPORT環境変数が公開されることを期待するので、package.jsonを書き換えます。
"start": "next start -p ${PORT}"がミソ。

package.json
{
  "name": "hello-next",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start -p ${PORT}"
  },
  "license": "ISC",
  "dependencies": {
    "isomorphic-unfetch": "^3.0.0",
    "next": "latest",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}

ここで、いったんちゃんと動くか試しておくと良さそうです。
shusaku-next-cloudrunは適当な名前なので好きな名前を付けてください。

docker build . -t shusaku-next-cloudrun:latest
PORT=8080 docker run -p8080:${PORT} shusaku-next-cloudrun

localhost:8080にアクセスしてレスポンスがあればOKです。

Cloud Registryへ登録

Buildしたdockerにtagを打って、Cloud Registryへ登録します。google cloud sdkはインストールしておいてください。

docker tag shusaku-next-cloudrun gcr.io/${PROJECT_NAME}/shusaku-next-cloudrun:latest
docker push gcr.io/${PROJECT_NAME}/shusaku-next-cloudrun:latest

Cloud Runへdeploy

この時いくつかコツがあります。

  • --allow-unauthenticated を付けておくと、デプロイした後に振られたURLで動作確認できます。
  • --projectはなくても動きますが、複数のprojectをgcloudコマンドで使っていると、defaultのconfigを読みに行ってしまってうまくいきません
  • 初めて実行する場合は、認証情報に関するエラーが出るので gcloud auth configure-dockerを実行する必要があるかも。
gcloud beta run deploy --image gcr.io/${PROJECT_NAME}/shusaku-next-cloudrun --project ${PROJECT_NAME} --platform managed --allow-unauthenticated

Browserからアクセス

URLが最後表示されるのでそこからアクセスしたらOK

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

Reactの環境構築

【React】npxでのReactの環境構築までのステップについて

create-react-appコマンドを使用

※注意事項:事前にnode.jsをインストールしてください。

まだインストールされていない方はこちらから!
node.js

1.ターミナルでコマンドを実行

①"ls"コマンドを入力してディレクトリがすでに存在しているかの確認をします。
 例)react-directory(お好きな名前)をディレクトリ名とする。

②"npx create-react-app react-directory"と入力をします。
スクリーンショット 2020-03-15 12.03.16.png

2.実行結果を確認

実行完了後、"ls"コマンドを入力して"react-directory"が実行できていることを確認します。
スクリーンショット 2020-03-15 12.04.34.png

3.packge.jsonを確認

①ダウンロードされたディレクトリにpackage.jsonがあることを確認します。
スクリーンショット 2020-03-15 12.13.16.png

②package.jsonの中身をエディターで確認します。
スクリーンショット 2020-03-15 12.14.24.png

4."npm start"でReactアプリを起動

エディタで"npm start"を入力してURLを出力する。
スクリーンショット 2020-03-15 12.24.36.png

5.環境構築成功

エディタで出力されたURLをクリックして画像のように表示されれば成功です。
お疲れ様でした!

image.png

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

【React】npxでのReactの環境構築までのステップについて

create-react-appコマンドを使用

※注意事項:事前にnode.jsをインストールしてください。

まだインストールされていない方はこちらから!
node.js

1.ターミナルでコマンドを実行

①"ls"コマンドを入力してディレクトリがすでに存在しているかの確認をします。
 例)react-directory(お好きな名前)をディレクトリ名とする。

②"npx create-react-app react-directory"と入力をします。

komorioBookpuro:~ komorishuhei$ npx create-react-app react-directory
npx: 99個のパッケージを5.733秒でインストールしました。

Creating a new React app in /Users/komorishuhei/reac-directory.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...

2.実行結果を確認

実行完了後、"ls"コマンドを入力して"react-directory"が実行できていることを確認します。

Success! Created react-directory at /Users/komorishuhei/react-directory
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd react-directory
  npm start

Happy hacking!

3.packge.jsonを確認

①ダウンロードされたディレクトリにpackage.jsonがあることを確認します。
スクリーンショット 2020-03-15 12.13.16.png

②package.jsonの中身をエディターで確認します。

{
  "name": "react-directory",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-scripts": "3.4.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

4."npm start"でReactアプリを起動

エディタで"npm start"を入力してURLを出力する。

Compiled successfully!

You can now view project in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://

Note that the development build is not optimized.
To create a production build, use npm run build.

5.環境構築成功

エディタで出力されたURLをクリックして画像のように表示されれば成功です。
お疲れ様でした!

image.png

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

[React]Hooks を使って無限スクロールを実装する

Reactでいろんなサンプルを作って理解しようとしてるんですが、その中で無限スクロールを作ってみたので Memo :pencil:

サンプルは こちら

サンプルの API はこちらの API を使わせてもらいました。
https://punkapi.com/

import React, { FC, useEffect } from 'react';
// setPageNumber でページを変えると fetch してくる hook。取得してきたものは beers にたまっていく
import useBeers from 'hooks/useBeers';
import lodash from 'lodash';

const InfiniteScroll: FC = () => {
  const { beers, setPageNumber } = useBeers();

  // 一番下に到達したら setPageNumber でページを更新
  const handleScroll = lodash.throttle(() => {
    if (
      window.innerHeight + document.documentElement.scrollTop !==
      document.documentElement.offsetHeight
    ) {
      return;
    }
    setPageNumber(prev => prev + 1);
  }, 200);

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <>
      <ul>
        {beers.map((beer: any) => (
          <li key={beer.id}>
            <img src={beer.imageSrc} />
            <div>{beer.title}</div>
          </li>
        ))}
      </ul>
    </>
  );
};

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

パパメモ

メモを保存するときの画面流れ

メモを書く
「貼り付ける」ボタンを押す
冷蔵庫に貼り付けられる

貼り付けたメモを押す
拡大して表示される

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

styled-components + TypeScript�でコンポーネントが拡張できなかった

今回の問題

コンポーネントを作成して別ファイルにインポートして拡張したかったができなかった。

Paragraph.tsx
const Paragraph = styled.p`
  color: red;
`

export default Paragraph

こんなコンポーネントを作成した。
これをApp.tsxにインポートして拡張しようとしてこんな風にした。

App.tsx
import Paragraph from 'Paragraph'

const StyledParagraph = styled(Paragraph)`
  color: blue;
`

export default () => {
  return (
    <StyledParagraph>hoge</StyledParagraph>
  )
}

理想の結果:hoge
実際の結果:hoge

なぜ青くならない…。

原因と解決方法

原因は元になるコンポーネントに拡張後のクラス名が反映されていなかった。

<p class="Paragraphのクラス名 StyledParagraphのクラス名">hoge</p>
こうなってほしいのにdeveloperToolで確認すると
<p class="Paragraphのクラス名">hoge</p>
こうなっていたのが原因だった。

そのため、Paragraph.tsxStyledParagraphのクラス名を受け取れるようにすればいいので、Paragraph.tsxを以下のように書き換えた。

Paragraph.tsx
interface propsInterface {
  className?: string
}

const Paragraph = styled.p`
  color: red;
`
const paragraph = (props: propsInterface) => {
  return <Paragraph className={props.className} />
}

export default paragraph

こうすることで拡張した後のstyleを反映させることができた。
実際の結果:hoge




あとがき
これがスマートで正しい解決策なのかは分からないが自分は解決することができたので、もし解決してない人がいれば参考にしてほしい。

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

React Material-UI Snackbarのカスタマイズに挑戦した

前提

React Material-UIのSnackbarコンポーネントアニメーションを自分好みにカスタマイズしてみまました。
React × Material-UIの記事が少ないので、内容コアですが、記事にしてみました。

Snackbar(スナックバー)とは

スナックバーは、実行プロセスをユーザーに通知するための表現です。
Image from Gyazo

こちらのスライドアニメーションのカスタマイズに挑戦します。

Snackbarコンポーネントの構造

Material-UIの構造の多くは、atom要素を構成しています。
今回のSnackbarも同様に、複数のatom要素でできています。

ReactのChrome拡張ツールなどでコンポーネント構造を見ると以下のようになっています。

<Snacbar>
  <ClickAwayListener>
    <Transition>
      <Slide> // デフォルトは<Grow>
        <SnackbarContent>

もちろん、利用時に、テキスト構造などSnackbarのコンポーネント内に渡してあげると構造は変化します。

APIについて

Snackbarコンポーネントに用意されたコンポーネントは主に以下があります。(公式サイト

  • action
    • スナックバーの最後にレンダリングされる要素
  • anchorOrigin
    • アンカー要素
  • autoHideDuration
    • スナックバーの表示時間
  • message
    • スナックバーに表示されるメッセージ
  • onClose
    • 閉じるリクエストが起こった場合のコールバック
  • open
    • 表示
  • TransitionComponent
    • トランジションに使用されるコンポーネント
  • TransitonProps
    • トランジション要素のプロパティ
  • transitonDuration
    • トランジション期間の設定

Snackbarの実装

特別な要素指定を行わずにSnackbarの組み込みをしてみました。

import React from 'react';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import Slide from '@material-ui/core/Slide';
import ClearIcon from '@material-ui/icons/Clear';

// FIXME:せっかくTransitionPropsプロパティあるのでそちらでdirection設定したい...
function Transition(props) {
  return <Slide {...props} direction="left" />;
}

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
  };
  const handleClose = () => {
    setOpen(false)
  };

  return (
    <div className="App">
      <Button variant="contained" onClick={handleOpen}>Register</Button>
      <Snackbar
        open={open}
        message="Complete!"
        onClose={handleClose}
        TransitionComponent={Transition}
        action={<ClearIcon onClick={handleClose}/>}
      />
    </div>
  );
}

TransitionPropsがよくわからない

Slideコンポーネントの組み込み方が気持ち悪いですが、なせか、TransitionPropsがうまく効きませんでした...
(原因わかったり、対応方法見つけたらまたそのタイミングで...わかる方いましたらコメントいただけると嬉しいです。。)

コードについて

Buttonコンポーネントの押されたタイミングでスナックバーが表示されます。
Snackbaractionに閉じるアイコンを設置してonClickイベントでスナックバーを閉じれるように設定しています。

SnackbaronCloseオプションを指定して、いると、スナックバー外の要素アクションでも閉じるようです。

アニメーションカスタマイズ

スナックバーでTransitionComponentを指定しなければGrowコンポーネントが標準で用意されているようです。
上のコードではSlideコンポーネントを使って横から入ってくるようにしています。

標準のTransitionが動くと、結構モーションが速いので、少しゆっくりめにカスタマイズしてみます。

transitonDurationプロパティ

こちらのプロパティでenterexit指定で入ってくるモーションと、出ていくモーションの指定が行えます。

transitionDuration={{
  enter: 800,
  exit: 800,
}}

モーション指定を行った後のソースは以下です。

import React from 'react';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import Slide from '@material-ui/core/Slide';
import ClearIcon from '@material-ui/icons/Clear';

// FIXME:せっかくTransitionPropsプロパティあるのでそちらでdirection設定したい...
function Transition(props) {
  return <Slide {...props} direction="left" />;
}

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
  };
  const handleClose = () => {
    setOpen(false)
  };

  return (
    <div className="App">
      <Button variant="contained" onClick={handleOpen}>Register</Button>
      <Snackbar
        open={open}
        message="Complete!"
        onClose={handleClose}
        TransitionComponent={Transition}
        transitionDuration={{
          enter: 800,
          exit: 800,
        }}
        action={<ClearIcon onClick={handleClose}/>}
      />
    </div>
  );
}

Image from Gyazo

アニメーションの変更ができました!

さいごに

Material-UIを使うと手軽に様々なモーション、インタラクションの活用ができてすごく便利ですが、そこに縛られてしまうこともあります。
この辺りの細かいオプションが用意されていればすごく利便性上がりますが、ここに苦しめられることも多くあります。

同じようなコンポーネントでも用途が違うコンポーネントとして用意されているものも多くあります。
皆さんはReact開発ではどんなコンポーネント開発行ってますか?是非聞かせて欲しい。。

2020/3/15 CodeSandbox リンク追加しました!

Edit material-ui-snackbar

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