20200908のReactに関する記事は10件です。

超初心者のReact(create-react-app)基礎学習覚え書き①

Reactの最大の利点はコンポーネントの使いまわしが可能なこと!!

基礎学習をしていて思ったのは編集の際に修正点が少ない。
CSSのSass(Scss)で変数にしたような感じに近いものを感じる。
はっきり理解できとらんが、備忘録として

create-react-app内のディレクトリー

1.src:コンポーネントを作るJSファイル
2.public:htmlファイルや設定ファイル
3.build:本番環境用のファイル
※他のディレクトリ(node_modulesなど)は割愛

実行コマンド

npm run build
srcとpublic内のファイルをバンドル(まとめて)buildディレクトリに出力

npm start
ローカルサーバーを起動してReactアプリを確認できる

npm run eject
babelやwebpackの設定を変更したい時に使用

Atomicデザイン

※これは余談
パーツコンポーネント単位で定義していくUIデザイン手法で、端的にはパーツの最小単位からデザインしていくこと

用語

state
・コンポーネントの"状態"を指す。Reactのコンポーネント内だけで使うものを「ローカルステート」と呼ぶ
※Reduxでは「グローバルステート」
・コンポーネント内で管理する変数
・propsを使用して親→子コンポーネントに渡せる

props
コンポーネント間でデータを受け渡すためのオブジェクト

コンポーネントの種類

Class Component

ClassComponent.jsx
import React from 'react';

class Hoge extends Rect.component {
    construtor(props) {
        super(props);
    }
    render() {
        return(
            <div>
                <h2>{this.props.hoge}</h2>
            </div>
        );
    }
}

クラスによって定義されたコンポーネント(見た目:複雑-記述量は増える)
(最近のReactではあまり使わないように記述する流れが多いらしい)
・関数ではないのでクラスとして宣言する
・React.Componentってやつを継承する
・stateってやつとライフサイクルをもつ
・propsってやつにはthisってやつが必要
・JSXはrenderメソッド内でreturnする

Functional Component

FunctionalComponent.jsx
import React from 'react';

const Hoge = (props) => {
    return (
        <div>
            <h2>{props.hoge}</h2>
        </div>
    );
};

関数型で定義されたコンポーネント(見た目:シンプル-記述量は少ない)
・JSX(HTMLっぽい書き方のやつ)をreturnする
・ES6のアロー関数で記述
・stateってやつを持たない(stateless)
・propsってやつを引数に受け取る

React.Fragment

JSX上でコンポーネントをラップする為だけに使われる
コンソールで確認した時にhtml上で無駄に<div></div>タグが多いときにつけるタグ
省略形として、ただの<></>でも良い

props

Functional Componentでは
propsを引数に受け取り、受け取った引数をprops.hogeというように呼び出せる
Class Componentでは
1."constructor"で初期化しなければならない
2.{this.props.hoge}のように記述する必要がある

propsで受け渡せるデータ型

1.{}内に記述 ※文字列のみ{}なしでも良い
2.文字列、数値、真偽、配列、オブジェクト変数

例文.
   title="HogeHoge"
   order={7}
   ispublished={true or false}
   builder={builderName}

state

stateが必要なわけ
1.render内では値変更は禁止の為
2.setState()メソッドで値変更の管理をする為
3.レンダー内でのstate変更は再レンダー(無限ループ)のきっかけになる為
→だからsetStateを使いなさいよ

stateの設定、取得、変更

設定

・Classコンポーネントの使用が前提
・constructor()内で宣言
・オブジェクト型で記述

取得

1.stateの取得は同一コンポーネント内で{this.state.hogeHoge}
で取得可能
2.子コンポーネントで参照したい場合はpropsとして渡す

変更

1.関数にラップする
2.setState()内に記述されたstateのみを変更する為、他のstateに変更を与える心配はない

state例文.jsx
class Blog extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isPublished: false
    }
  }

  //公開状態の反転
  togglePublished = () => {
    this.setState({
      isPublished: !this.state.isPublished
    })
  };

  render() {
    return (
      <>
        <Article title={"React基礎"} isPublished={this.state.isPublished} toggle={() => this.togglePublished()}/>
      </>
    )
  }
}

export default Hoge

続きは②

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

今更ながら graphql-code-generator の便利さを痛感する

はじめに

今日も今日とて、フロントReact + バックRailsのSPA + APIのアプリ開発していたところ

TypeScriptのReact側で、react-apolloの型宣言がめんどくさいと思っていました。

バックエンド側はGraphQLを使用しているので、いろんなところに型宣言をしているようにも感じて、微妙。。

そこでgraphql-code-generatorを使っていろいろ気持ち悪い部分を解消していこうという話をします。

今回の構成

フロントエンド

GitHub

  • React(SPAで)
  • TypeScript
  • create-react-app
  • React Apollo

バックエンド

GitHub

  • Ruby
  • Rails(APIで)
  • GraphQL

※上記2つのリポジトリはこちらのリポジトリから連動させる仕組みとしました。(開発環境として)

とりあえずApolloの公式通りにやってみる

バックエンド側にtodosという、Todoモデルにあるデータを全て取得するAPIを作成しておきました。

これをフロントエンド側で取得し、表示します。

src/App.tsx
+import { gql, useQuery } from "@apollo/client";
 import React from "react";
 import logo from "./logo.svg";
 import "./App.css";

+const TODOS_QUERY = gql`
+  query {
+    todos {
+      name
+   }
+  }
+`;

 const App = () => {
+   const { loading, data } = useQuery(TODOS_QUERY);
+
   return (
     <div className="App">
       <header className="App-header">
         <img src={logo} className="App-logo" alt="logo" />
         <p>
           Edit <code>src/App.tsx</code> and save to reload.
         </p>
-        <a
-          className="App-link"
-          href="https://reactjs.org"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          Learn React
-        </a>
+        {loading ? (
+          <p>Loading ...</p>
+        ) : (
+          <ul>
+            {data && data.todos.map(({ name }, i) => <li key={i}>{name}</li>)}
+          </ul>
+        )}
       </header>
     </div>
   );
 };

 export default App;

型宣言していないので、エラーが出ましたね。

スクリーンショット 2020-08-13 21.10.37.png

型宣言してあげます。

用意する型は、Todoモデルの型と、レスポンス値の型です。

レスポンスは{"data":{"todos": []}という値が返るようにしています。

interface Todo {
  name: string;
}

interface TodosData {
  todos: Todo[];
}

TodosDataを以下のように使います。

-  const { loading, data } = useQuery(TODOS_QUERY);
+  const { loading, data } = useQuery<TodosData>(TODOS_QUERY);

エラーなく実行できました。

スクリーンショット 2020-08-13 21.15.03.png

では、GraphQL Code Generatorが入っていたらどうなるか試してみます。

GraphQL Code Generatorを使う

公式の手順通り、インストールとセットアップ

https://graphql-code-generator.com/docs/getting-started/codegen-config

$ yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations
package.json
"scripts": {
  "generate": "graphql-codegen"
}

バックエンド側に用意しているエンドボインとはhttp://localhost:5000/graphqlなので、schemaにこれを使います。

codegen.yml
schema: http://localhost:5000/graphql
documents: ./graphql/queries/*.graphql
generates:
  ./src/types.d.ts:
    plugins:
      - typescript
      - typescript-operations

documentsオプションを使用する場合に、@graphql-codegen/typescript-operationsが必要みたいです。

documentsに指定した場所に、todosのクエリを記載します。

graphql/queries/todos.graphql
query {
  todos {
    name
  }
}

バックエンドを起動してある状態で、用意したgenerateコマンドを実行してみます。

$ yarn generate

src/types.d.tsファイルが生成されました。

src/types.d.ts
...省略...

export type Unnamed_1_QueryVariables = Exact<{ [key: string]: never; }>;


export type Unnamed_1_Query = (
  { __typename?: 'Query' }
  & { todos: Array<(
    { __typename?: 'Todo' }
    & Pick<Todo, 'name'>
  )> }
);

Unnamedとなってしまっているので、クエリに名前をつけて再度実行します。

graphql/queries/todos.graphql
-query {
+query todos {
   todos {
     name
   }
 }

src/types.d.tsファイルにTodosQueryというTypeが定義されました。

これをuseQueryの型に利用してみます。

src/App.tsx
+ import { TodosQuery } from "./types.d";

- const { loading, data } = useQuery<TodosData>(TODOS_QUERY);
+ const { loading, data } = useQuery<TodosQuery>(TODOS_QUERY);

同じように動作が確認できました。

スクリーンショット 2020-08-16 22.05.54.png

src/App.tsxTodoモデルの型と、レスポンス値の型を定義しなくて良くなりました。

でも、TODOデータを取得する為のクエリをsrc/App.tsxgraphql/queries/todos.graphqlの2箇所に書いているのが気持ち悪いですよね。

todosを取得する為の専用のuseQueryがあれば型もクエリも渡さなく済むのに。。。

@graphql-codegen/typescript-react-apolloを導入する

todosを取得する為の専用のuseQueryがあれば型もクエリも渡さなく済むのに。。。

ということで、この気持ち悪いを解消していきます。

まずはインストール

$ yarn add -D @graphql-codegen/typescript-react-apollo

typescript-react-apolloを追加します。

codegen.yml
 schema: http://localhost:5000/graphql
 documents: ./graphql/queries/*.graphql
 generates:
   ./src/types.d.ts:
     plugins:
       - typescript
       - typescript-operations
+      - typescript-react-apollo

生成コマンドを実行

$ yarn generate

src/types.d.tsuseTodosQueryという関数が生成されたので使ってみます。

src/App.tsx
- import { TodosQuery } from "./types.d";
+ import { useTodosQuery } from "./types.d";

- const { loading, data } = useQuery<TodosQuery>(TODOS_QUERY);
+ const { loading, data } = useTodosQuery();

ちゃんと動きましたね。

スクリーンショット 2020-08-16 22.29.40.png

GraphQLの便利なところとして、同じAPIでも、必要なフィールドのみを取得することができる特徴があります。

先ほど、todosのデータを取得する際、nameのみ指定し取得、一覧表示のような機能を実現しました。

これを個別に、編集、削除といった機能を実現するには、nameがユニークでない限り、idのようなもので、

todoを特定する必要があります。

別の画面等で、nameに加え、idも必要な場面があった場合、以下のようなクエリを別で作成したくなってきます。

query todos {
  todos {
    id
    name
  }
}

しかし、graphql-code-generatorで生成する関数は全て、src/types.d.tsに入るように設定しています。

ここには既に、nameのみを指定したtodosを取得するクエリの関数が存在しているので、以下のようなファイルを作成し、

yarn generateを実行すると、Not all operations have an unique nameというエラーが発生します。

graphql/queries/todosIncludeId.graphql
query todos {
  todos {
    id
    name
  }
}

queryの右に記載している名前が、ユニークでないといけないってことですね。

graphql/queries/todosIncludeId.graphql
-query todos {
+query todosIncludeId {
   todos {
     id
     name
   }
 }

queryの名前をユニークな名前に変更してみました。

すると、src/types.d.tsuseTodosQueryとは別に、useTodosIncludeIdQuery関数が生成されました。

GraphQLの便利な特性を潰すことなく利用できますね。 :thumbsup:

参考文献

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

Azure Static WebApps で react URL 直打ち

routes.jsonの配置

static webapps 用のルーティング設定ファイル『routes.json』をpublicに配置して、デプロイすればOK
2020-09-08 215916.png

{
    "routes": [
        {
            "route": "/*",
            "serve": "/index.html",
            "statusCode": 200
        }
    ]
}

MSのサイト

開発環境なら別にいいけど、サイトにのっけると CSR なので、index.html に来てもらわないと、href でサイトの相対パス設定しても 404 なります

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

ReactとTSでMaterial Designのアプリのセットアップをする手順

  1. アプリを作る yarn create react-app my-app --template typescript
  2. Material UIを追加する yarn add @material-ui/core
  3. Robotoをインストールする yarn add fontsource-roboto
  4. index.tsxに import 'fontsource-roboto';を追加
  5. Iconsを追加 yarn add @material-ui/icons
  6. 使いたいアイコンをインポートimport { AccessAlarm, ThreeDRotation } from '@material-ui/icons';
  7. routerをインストール yarn add react-router-dom && yarn add @types/react-router-dom
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React.useRef】イベントリスナー内で最新のステートを参照できない時の対処法

はじめに

関数コンポーネントのイベントリスナー内で、State(ステート/状態)が正しく扱えない場合の対処方法です。

React.useState()のステートを、イベントリスナーのコールバック関数内で参照しようとすると、値が最新の状態になっていないことがあります。

うまくいかない例

  1. counterというStateを宣言
  2. ボタンをクリックするとcounter+1
  3. クリックに反応したイベントリスナーがfunc()を発火
  4. counterの値を出力
関数コンポーネント
// ①
const [counter, setCounter] = React.useState(0);

// ④
const func = () => {
  console.log(counter);
};

// ③
React.useEffect(() => {
  window.addEventListener("click", func);
}, []);

// ②
return (
  <button onClick={() => setCounter(counter + 1)}>
    ボタン
  </button>

この例では、④ console.log(counter)で出力される値は常に0です。
イベントリスナーの登録時点のcounterの値で固定されてしまい、ステートが更新されても出力は変わりません。

これでは何回クリックしたかわからない...。

useRef()を使う

React.useRef()を使うことで、常に最新の状態を参照することができます。

うまくいく例
const [counter, setCounter] = React.useState(0);

// 追加
const counterRef = useRef(); // refオブジェクトを作成
counterRef.current = counter; // refはcounterを参照する


const func = () => {
  // refを出力する
  console.log(counterRef.current);
};

// -----以下は変更なし-----
React.useEffect(() => {
  window.addEventListener("click", func);
}, []);

return (
  <button onClick={() => setCounter(counter + 1)}>
    ボタン
  </button>

コールバック内でcounterを使うかわりに、counterRef.currentを使用します。
このようにすることで、最新のステートを参照することができます。

まとめ

関数コンポーネントのイベントリスナー内で、最新のstateが参照できない場合は、コールバック内でStateを使うかわりに、Stateを参照するRefを使用します。

このようにすることで、最新のステートを参照することができます。

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

Gatsby + GitHub Pages でポートフォリオページを無料でシュッと作る

エンジニアたるもの、自分のポートフォリオページは持っておきたいですよね。
Gatsby + GitHub Pages を利用したポートフォリオページ作成手順をまとめましたのでご活用ください。

こんなページをシュッと作れます。維持費もかかりません。
Image from Gyazo

GitHubでポートフォリオ用リポジトリ作成

まずはポートフォリオ用のリポジトリを作成していきます。

  • GitHub右上のメニューから 「New repository」をクリック

image.png

  • Repository name に <ユーザ名>.github.io を入力し、 「Initialize this repository with a README」にチェックつけて「Create repository」をクリック

image.png

  • リポジトリ作成完了。Clone with SSH をコピーしておきます

image.png

  • ターミナルで作成したリポジトリをチェックアウト
# <user>を置き換えて!!!
$ git clone git@github.com:<user>/<user>.github.io.git && cd <user>.github.io

Gatsbyで静的サイト作成

  • Gatsby CLI インストール
$ npm install -g gatsby-cli
$ gatsby new gatsby-simplefolio https://github.com/cobidev/gatsby-simplefolio
  • gatsby new が終わったらサイトのディレクトリへ移動
$ cd gatsby-simplefolio
  • 開発環境用サーバ起動
$ gatsby develop
  • http://localhost:8000/ へアクセスし、Gatsbyで作成した静的サイトを確認。早いですね。

image.png

作成した静的サイトを GitHub pages として公開

実は <user>.github.ioの命名規則でリポジトリを作成した時点で自動的にGitHub Pagesとして公開されています。
↓こんな感じ。READMEの内容が表示されてますね。
image.png

ページのカスタマイズをする前に、先ほどGatsbyで作成したページを一旦そのままGitHub Pagesとして公開してみます。

  • package.json にデプロイ用コマンド追加
gatsby-simplefolio/package.json
     "start": "npm run develop",
     "serve": "gatsby serve",
     "clean": "gatsby clean",
-    "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
+    "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1",
+    "deploy": "gatsby build && cp -pvr public/* ../ && git add ../ && git commit -m 'Deploy to production' && git push"
   },
  • デプロイコマンド実行
$ yarn run deploy
  • しばらくすると GitHub Pagesに反映されます

image.png

カラーテーマ変更

  • サイト全体のカラーテーマを変更することができます

  • https://uigradients.com/ で好みの色を見つけましょう。

Image from Gyazo

  • 気に入ったグラディエーションのカラーコードを以下のファイルに反映すればOKです。
gatsby-simplefolio/src/style/abstracts/_variables.scss
// COLORS
$primary-color: #02aab0;
$secondary-color: #00cdac;

コンテンツ内容設定して完成!

  • 中身はただのReactです。タグ定義なんかも自由にカスタマイズしていきましょう!

Image from Gyazo

独自ドメインを設定

以降の手順は任意です。お金がかかるケースもあります :money_with_wings:

image.png

  • リポジトリのSettingsから、Custom domain に設定したいドメインを入力してSave
    image.png

  • CNAME というファイルが作成されます。設定ページからではなく手動でこのファイルを作成してもOKです。

image.png

  • これで、独自ドメインでポートフォリオページを表示できるようになります!

https://mishiwata1015.com/
※スマホ対応できてないです

image.png

参考

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

ドメイン駆動設計における5層アーキテクチャの全体図とソースの実装例

今回の記事では、過去に記述した以下のバックエンドに関する記事を一つに統合した内容となっている。
従来のドメイン駆動設計4層アーキテクチャにDCIアーキテクチャを組み込んでいく過程で、様々なパラダイムが発生し、
最終的に4層アーキテクチャミッション層を追加した5層アーキテクチャという形に落ち着いた。
今回の記事では、投稿した過去の記事の内容を5層アーキテクチャによって概念を整理し、整合性を担保している。

投稿した過去の記事一覧

・ドメイン駆動設計のユースケース層(アプリケーション層)を軍事思想(作戦術)でドーピング
https://qiita.com/aLtrh3IpQEnXKN7/items/853ecb3cd109dd016476

・OOUI(オブジェクト指向設計)によるバックエンド設計のパラダイムシフト
https://qiita.com/aLtrh3IpQEnXKN7/items/3aa05f9628544c43f279

・DCIアーキテクチャの活用方法を紹介
https://qiita.com/aLtrh3IpQEnXKN7/items/355ad12f82ac424abea3

・エリック・エヴァンスが提唱したアーキテクチャの4層モデルを拡張する
https://qiita.com/aLtrh3IpQEnXKN7/items/b7fe2014ccefcbb9e458

・ドメイン駆動設計のミッション層を設計する手法を紹介
https://qiita.com/aLtrh3IpQEnXKN7/items/98e0c2d2ee0776e0e039

・5層アーキテクチャモデルにおけるドメイン分析
https://qiita.com/aLtrh3IpQEnXKN7/items/e5d1a276c07f1fe140be

各レイヤーの説明

レイヤーの全体図

image.png

1.コントロール層

アプリ外からアクセスの入り口となるインターフェースを主にRestApiで提供する。アプリ外からのアクセスがWeb、iOS、Androidと増加するたびにインターフェースを追加する必要がある。
インターフェース以外の機能として、コントロール層からユースケース層へデータを受け渡す際のデータ変換、コントロール層からアクセス元へのデータ変換(JSON化)を行う。データの受け渡し、データ変換機能をメインに担当する。

2.ユースケース層

ビジネスロジックで実現したい目的を記載するための層。ミッション層のメソッドの呼び出しのみを行う。
ユースケース層では、フロントエンドから取得したデータを格納するためのBeanクラスを必ず用意する。
Beanクラスミッション層へ引数として渡し、ミッション層内で具体的なロジック内容を実装する。

ユースケース層のビジネスロジックは以下の種類で実装する。
偵察・・・画面に表示するデータを取得する
順次・・・ビジネスのワークフローを順番通りに進める
ゲリラ・・・不定期に行われるイベント処理を実施する
監視・・・DB内部の情報を監視し、アプリの規約に違反したユーザーの発見などを行う
エスカレーション・・・アプリのルールに違反しているユーザーへの通知、警告などを行う
エマージェンシ・・・緊急事態に対応する
相対・・・キャンセル処理などビジネスのワークフローから外れる例外処理

3.ミッション層

具体的なビジネスロジックを記述するための層。ビジネスロジックはDCIアーキテクチャによって実装が行われる。
この層では、ユースケース層で実現したい目的をドメイン層のメソッドを組み合わせて実現する。
以下の観点で、ドメイン層のメソッドを組み合わせロジックを作成する。

イベント・・・DBへデータを書き込む事象
リソース・・・企業が取り扱うサービス or 商品
ルール・・・イベントに紐づく運用ルール
テクノロジー・・外部ライブラリを使用して実装されるメールの転送やファイルアップロード
エージェント・・・ミッション層のロジックを統率する。イベント、リソース、テクノロジーのメソッドが全て集約する。

4.ドメイン層

ミッション層のロジックを構成する層。ミッション層内でメソッドの呼び出しが行われる。
ドメイン層では、アプリ全体で共通するデータ構造の取得、共通するデータ構造に依存した業務ルールの提供、データ集約に基づいたデータ登録 or 更新 or 削除処理を取り扱う。

5.インフラ層

外部ライブラリを使用したロジックを記述し、全レイヤーに外部ライブラリの機能を提供する。例としてメールの送信、ログの出力などが該当する。
ライブラリはバージョンアップや使用するライブラリ変更されるなど、頻繁に発生する箇所する。そのため、DIP(依存関係逆転の原則)に従って、
ドメイン層にインターフェースを配置し、実際のロジックはインフラ層に記述する。インターフェースをデータインジェクション(依存性注入)機能を利用してインタンス生成を行う。

実装するクラス一覧

各レイヤー層に記述するクラス一覧について記載

1.コントロール層

Controller・・・REST APIの提供、Convetクラスのメソッドの呼び出し、ユースケース層の画面レベルのメソッドの呼び出し、
Web用、Andoroid、iOSへ引数を返却する際の値変換(JSON化)など
Coverter・・・REST APIから取得した画面の入力値の値をユースケース層へ引き渡す際の値の変換

2.ユースケース層

Bean・・・コントロール層から取得した入力値を格納して、ユースケース層へ引き渡す際のクラス。getter setterでメソッドを構成。表示画面と1対1の関係にある。
SearchOperation・・・検索したデータを画面表示することを目的としたビジネスロジック
RegularOperation・・・ビジネスのワークフローを次状態に進めるデータ登録、更新処理を目的としたビジネスロジック
GuerrillaOperation・・・ゲリライベントで発生するデータ登録、更新、削除処理を目的としたビジネスロジック
MonitoringOperation・・・DBのデータを監視し、規約違反が発見した場合、規約違反のデータ登録、更新、削除処理を目的としたビジネスロジック
EscalationOperation・・・エスカレーションラダーを設定し、規約違反を起こしているユーザーに各エスカレーションに応じた通知処理を目的としたビジネスロジック
EmergencyOperation・・・緊急事態対応を目的としたビジネスロジック
IrregularOperation・・・従来のビジネスのワークフローから外れるデータ登録、更新、削除処理を目的としたビジネスロジック

3.ミッション層

Mission ・・・Beanクラスを引数として受け取り、Beanクラスに対応したActorクラスのインスタンス、Actorクラスの型となるRoleインターフェスの選択、Roleメソッドの実行を行う。
Actor・・・Roleインターフェースを実装するクラス。Mixinを使用して多重継承を実施。
Role・・・インターフェース。ミッション層のEvent、Resource、Rule、Technologyのメソッドを集約する。ミッション層のロジックを組み合わせ ユースケース層で実現したいロジックを実装する。
Event・・・DBの登録 or 更新 or 削除に関する事象を実装するクラス。ドメイン層の、Aggregates、Servicesの組み合わせで実装される。
Resource・・・企業が取り扱う商品 or サービスのデータをDBから取得するクラス。ドメイン層の、Value Objects、Factories、Entities、Repositoriesの組み合わせで実装される。
Rule・・・運用ルールを実現するクラス。ドメイン層の、Specification、Value Objects、Factories、Entities、Repositoriesなどのメソッドの組み合わせで運用ルールを実現する。
Technology・・・技術的なビジネスロジックを実装するクラス。インフラ層のメソッドとインフラ層のメソッドを組み合わせて実現する。

4.ドメイン層

Factorys・・・DTOクラスを引数とし、ValueObjectインスタンスを生成
ValueObject・・・共通構造クラス。DTOをフィールド変数として持ち、getterメソッドとisメソッドのみで構成されている。DTOのデータを加工して提供、DTOのデータに応じたif分の提供などを行う。
Specifications・・・ビジネスルール or 入力値チェックなどのビジネスロジックを提供する
Repositorys・・・DBからデータ検索処理を行うクラス。
Aggregates・・・特定のデータ範囲に応じて、データの登録、更新、削除を行うクラス。
Services・・・上記で上げたドメイン層のロジックに分類できない処理を取り扱うクラス。

5.ドメイン層

Mail・・・メール送信を行う。
SFTP・・・SFTPによるデータ送受信を行う。
FileUpload・・・ファイルアップロードを行う。

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

【React × Firebase】おそらく世界一シンプルなログイン機能実装【サインイン・サインアウト】

はじめに

この記事では、React×Firebaseを使用して世界一シンプルにログイン機能を実装します。
デザインは全てMaterial-UIにお任せし、機能のみを追求しました。
それでは、張り切っていきましょう!

環境構築

なにがともあれ、create-react-appで環境構築しましょう!

create-react-app [app-name]

そして、必要なライブラリをinstall

npm install --save-dev firebase react-router-dom @material-ui/core

では、次にFirebaseのアプリを作成します。

Firebaseと接続

まずはFirebaseにアクセスし、アプリを作成してください。
下記、手順となります。

Component作成

Firebase.auth()

ユーザーを作成しよう!

ログインしよう!

最後に

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

Hasura, Apollo, Reactでsubscriptionsのリアルタイム更新の実装

メモ書き
需要が有れば書き直します。

実装コード

httpの通信からWebSocketの通信に切り替える必要があるのでプラグインのインストール

yarn add subscriptions-transport-ws

WebSocketLinkを使ってApolloClientのlinkをwsに変える

uriでhttp指定していたURIがhttp://localhost:8080/v1/graphqlであった
WebSocketで双方向に通信したいのでws://localhost:8080/v1/graphqlに書き換える。

この時、Hasura側では特に何もする必要はない。

index.tsx
import {
  ApolloClient,
  InMemoryCache,
  gql
} from "@apollo/client";
import { WebSocketLink } from "@apollo/client/link/ws";
import { useSubscription } from '@apollo/react-hooks'
import ArticleCards from '../components/ArticleCards'

const client = new ApolloClient({
  // uriをlinkに変更(http通信からws通信にする)
  link: new WebSocketLink({
    uri: 'ws://localhost:8080/v1/graphql',
    options: {
      reconnect: true,
       connectionParams: {
          headers: {
            // 認証系はここに書く
          }
       }
    }
  }),
  cache: new InMemoryCache(),
 });

export default function Home() {
  return (
    <ApolloProvider client={client}>
      <ArticleCards></ArticleCards>
    </ApolloProvider>
  );
}

記事一覧のコンポーネント(雑につくった)でリアルタイム更新

useQueryを使っていた場合は、useSubscriptionに書き換える。
gqlのコード以外は特に変わらないので説明は割愛

ArticleCards.tsx
import { useSubscription } from '@apollo/react-hooks'
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import articleStyle from '../styles/ArticleCards.module.css';

import { ARTICLES_SUBSCRIPTIONS } from '../gql/articles';

interface ArticleType {
  id: string;
  text: string;
}

const Article = (article: ArticleType) => {
  return (
    <Card variant="outlined" className={articleStyle.card}>
      <CardContent>
        <Typography variant="body2" component="p">
          {article.text}
        </Typography>
      </CardContent>
      <CardActions>
        <Button size="small">MORE</Button>
      </CardActions>
    </Card>
  );
}

const ArticleCards = () => {
  const { loading, error, data} = useSubscription(ARTICLES_SUBSCRIPTIONS);
  console.log(data)
  // ローディング中の表示
  if (loading) return <p>loading</p>
  // エラー時の表示
  if (error) return <p>{error.toString()}</p>
  // 成功してデータが帰ってきた時の表示
  return (
    <div className={articleStyle.article}>
      {data.articles.map((article: ArticleType) => (
        <Article {...article} key={article.id}></Article>
      ))}
    </div>
  );
}

export default ArticleCards

gqlでsubscriptionsを書く

参考までにquery, subscription, mutationを書いておいた。

gql/articles.ts
import gql from "graphql-tag";

export const ARTICLES_QUERY = gql`
  query {
    articles {
      id
      text
    }
  }
`;

export const ARTICLES_SUBSCRIPTIONS = gql`
  subscription ArticlesSubscriptions {
    articles(limit: 30, order_by: { created_at: asc }) {
      date
      id
      text
    }
  }
`;

export const ADD_ARTICLES = gql`
  mutation AddArticles($text: String = "", $date: date = "") {
    insert_articles(objects: { text: $text, date: $date }) {
      returning {
        id
        text
        date
      }
    }
  }
`;

参考記事

Hasura/GraphQL/React.js/Subscriptions
https://hasura.io/learn/graphql/react/subscriptions/1-subscription/

Hasura/GraphQL/Vue.js/Subscriptions
https://hasura.io/learn/graphql/vue/subscriptions/1-subscription/

WebSocketとは?
https://www.keicode.com/script/html5-websocket-1.php

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

gatsby入門 ブログ作ってサーバーにアップしてみる

gatsbyの作業履歴

gatsby入門 チュートリアルをこなす 0.開発環境をセットアップする
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(1)
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(2)
gatsby入門 チュートリアルをこなす 2. ギャツビーのスタイリングの概要
gatsby入門 チュートリアルをこなす 3. ネストされたレイアウトコンポーネントの作成
gatsby入門 チュートリアルをこなす 4. ギャツビーのデータ
gatsby入門 チュートリアルをこなす 5. ソースプラグインとクエリされたデータのレンダリング
gatsby入門 チュートリアルをこなす 6. 変圧器プラグイン※Transformer pluginsのgoogle翻訳
gatsby入門 チュートリアルをこなす 7. プログラムでデータからページを作成する
gatsby入門 チュートリアルをこなす 8. 公開するサイトの準備
今回:gatsby入門 ブログ作ってサーバーにアップしてみる

基本のチュートリアルはこなした。。。

さて何しましょう?とチュートリアルを見ているとgithubにブログ用のソースが上がっているとの記載が
https://github.com/gatsbyjs/gatsby-starter-blog
とりあえずブログでも作ってみっか!
んで、netlifyとgithubを使うのが主流みたいだけど、nginxの勉強もしたいので
とりあえず自分のGCPのVM環境にアップしてみようと思います。
飽きたらGCPから移行するかな?

ブログを構築

22:00作業開始 ローカルにgatsbyサイトを作成

まずは、ローカルにgatsbyサイトを作成します。
以下コマンドを好きなディレクトリで実行。そして風呂。
gatsby new 3s-laboo-blog https://github.com/gatsbyjs/gatsby-starter-blog
※3s-laboo-blogは任意です。
2020-09-07_22h03_24.jpg
なんか、ダウンロードすげぇ時間かかったな。
ダウンロード後サイトを開発モードで起動しながら、ビールをプシュっと。
cd 3s-laboo-blog
gatsby develop
2020-09-07_22h48_42.jpg
http://localhost:8000/
2020-09-07_22h50_15.jpg
おー。。。超シンプル。
まぁ、こっからアレンジすればいいが、今回は最低限にだけとどめよう。
ビール、グビグビ。

22:50 ブログをちょっとだけ修正

まず、顔が私じゃない!
content/assets/profile-pic.jpgを変更
あとアイコン
content/assets/gatsby-icon.pngを変更
2020-09-07_22h54_06.jpg
確認するとアイコンだけ出ていない。
gatsby-config.jsを少しいじってみる。
2020-09-07_23h35_21.jpg
2020-09-07_23h37_08.jpg
リスタートされたみたい。
改行されてないな。
src/stylesディレクトリを追加
src/styles/global.cssを追加し、以下を記述

src/styles/global.css
html {
    background-color: #e0ffff;
    white-space: pre-wrap;
}

pre-wrapは改行コードが反映されるよ!
gatsby-browser.jsを修正

gatsby-browser.js
// custom typefaces
import "typeface-montserrat"
import "typeface-merriweather"
import "prismjs/themes/prism.css"
import "./src/styles/global.css"←これ追加

2020-09-07_23h46_45.jpg
あと、マークダウンファイルをちょいと直して。。。
2020-09-07_23h52_34.jpg
とりあえずはこんなとこでしょ!
(後でかなり修正しないとな。。。)
ビールなくなった。
とりあえずコンパイル・起動
gatsby build
gatsby serve
動確問題ない。

00:00 winscpでgcpにログインし、nginxのフォルダにアップロード

nginxの配置フォルダはnginxで設定したフォルダです。
アップロードするものは、newしたディレクトリ(ここでいう3s-laboo-blog直下)にあるpublicディレクトリ内のファイル・ディレクトリ。
https://3s-laboo.com/
2020-09-08_00h43_41.jpg
イエーイ!
ということでわたくし3S Labooのブログを立ち上がりました。
時刻は00:46
風呂入ってビール飲んで歯を磨いたのも含めて3時間位で作れたー

今後修正していきまーす。
今回はここまで。

ありがとうございました。

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