2019年03月16日

ReactでSSR

実験的にReactでSSRを行っていく。まずはローカルでExpressサーバーを立ててSSRを行う。

まずは普通にReactでページを表示



まずはクライアントのJS用に以下のコマンドで各種ツールをインストール

npm init -y
npm install --save-dev webpack
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react
npm install react react-dom


package.jsonのscriptsにはビルド用のコマンドを設定、babelの設定も追加しておく

  "scripts": {
"build": "webpack --mode development",
"start": "webpack --mode development -w",
"release": "webpack --mode production"
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
},



webpack.config.jsを作成

const path = require('path');

module.exports = {
entry: "./src/script.js",
output: {
path:path.resolve(__dirname, "dist"),
filename: "script.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};


src/App.jsにAppコンポーネントを定義して

import React from 'react';

export const App = () => <h1>Hello React</h1>


src/script.jsにAppコンポーネントを出力するReactを書いて

import React from 'react';
import ReactDOM from 'react-dom';
import { App } from 'App';

ReactDOM.render(
<App />,
document.getElementById('app')
);




dist/index.htmlを設定

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./script.js"></script>
</body>
</html>


これでnpm run buildしてdist/index.htmlを表示するとhello worldが書き出されています。

サーバーの準備



次にサーバーの準備をしていきましょう。今回は後でSSRするためにnode.jsのサーバーExpressを用意します。

まずはExpressのインストール

npm install express


サーバー用の設定ファイルserver.jsを作成します。

'use strict'

const express = require('express')
const app = express()

app.use(express.static('dist'));

app.listen(3000)


dist/index.htmlがルートディレクトリになるように設定します。

ターミナルから以下のコマンドを打つと

node ./server.js


localhost:3000でHello React!が表示されます。

サーバーサイドでSSR



ここからが本題、サーバーサイドでSSRを行っていきます。

まずはJSXやES2015が解釈できるようにserver.jsをWebpackでコンパイルするようにします。

Webpackでnode.jsが扱えるようにwebpack-node-externalsをインストールして

npm install --save-dev webpack-node-externals


サーバー用の設定ファイルを以下のように定義します。

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
mode: 'production',
entry: "./server.js",
target: 'node',
output: {
path:path.resolve(__dirname, ""),
filename: "./server.dist.js"
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};


これで./server.jsの内容をコンパイルしてserver.dist.jsに出力できるようになります。

package.jsonのscriptsに以下のコマンドを追加しておくと

  "scripts": {
・・・
"server": "npm run build:server && node ./server.dist.js",
"build:server": "webpack --config webpack.config.server.js"
},


npm run serverのコマンドでスクリプトのビルドとサーバーの起動をやってくれます。

server.jsは次のように変更します。

import  express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import fs from 'fs'
import path from 'path'

import { App } from './src/App.js'

const app = express()

app.use(express.static('./dist'));

app.get('/', (req, res) => {
fs.readFile(path.resolve('./dist/index.html'), 'utf8', (err, data) => {
return res.send(
data.replace(
'<div id="app"></div>',
`<div id="app">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
})

app.listen(3000)


ReactDOMServer.renderToString()でAppコンポーネントの内容がHTMLに変換されて<div id="app"></div>内に挿入されます。

書き出されるHTMLはこんな感じに。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"><h1 data-reactroot="">Hello React</h1></div>
<script src="./script.js"></script>
</body>
</html>


ひとまず今回はここまで。次回はルーティング周りの対応を入れていきます。
posted by ねこまんま at 11:48 | React | 更新情報をチェックする

2019年01月14日

FirebaseHostingを利用してホスティング環境をデプロイする

Firebaseでプロジェクトを作成して左メニューからHostingを有効にしておく、あとはコマンドラインから。

まずはFirebas toolsをインストールしておく

npm install -g firebase-tools


まずはログイン

firebase login


以下のように聞かれるのでyesを選択

? Allow Firebase to collect anonymous CLI usage and error report
ing information?


ブラウザが開くのでFirebaseを利用しているGoogleのアカウントを選択しよう。

firebase init


ここでhostingを選択して、Firebaseでプロジェクトが選択できる場合は選択、選択できない場合は[don't setup a default project]を選んでおく ( あとでFirebase use −addコマンドで追加できる

デプロイするディレクトリとすべてのURLのエイリアスを/index.htmlにするSPAモードの選択ができる

? What do you want to use as your public directory? 
? Configure as a single-page app (rewrite all urls to /index.htm
l)?


これで、firebase.jsonファイルと.firebasercファイルが作成される

あとは以下のコマンドでデプロイできるはず

firebase deploy





posted by ねこまんま at 16:29 | Firebase | 更新情報をチェックする

Vue CLIを使ってVueの環境を作成する

まずはvue-cliをグローバルにインストール

npm install -g vue-cli


インストール内容を exec $SHELL -l で反映すればvueコマンドが利用できるので -h オプションでヘルプを表示してみましょう

exec $SHELL -l
vue -h


vue init コマンドでプロジェクトを作成

vue init webpack-simple vue-cli-sample


インストールができたらディレクトリに移動してnpmのインストールと実行を行います

cd vue-cli-sample
npm install
npm run dev


http://localhost:8080/ に移動するとVueプロジェクトが実行されているのを確認できます。

src/App.js のmsgなどを変更するとhttp://localhost:8080/ も変更されるのを確認することができます。

<script>
export default {
name: 'app',
data () {
return {
msg: 'Vue CLIを使ってVueの環境を作成する'
}
}
}
</script>


posted by ねこまんま at 15:32 | vue.js | 更新情報をチェックする

2019年01月12日

FirebaseとNuxt.jsを使ってユーザ認証関係2

FirebaseとNuxt.jsを使ってユーザ認証関係の続き。

前回はメールアドレスとパスワードを入力してアカウントを作成したが、今回はメールアドレス入力→確認メール→認証画面というフローでユーザ認証ができるように変更していきます。
posted by ねこまんま at 13:22 | Firebase | 更新情報をチェックする

2019年01月09日

TypeScriptのreadonly修飾子とReadonly型

TypeScriptではプロパティにreadonly修飾子とつけることで再書き込みができないプロパティを定義することができます。

interface Foo {
readonly prop: string
}
const foo: Foo = {
prop: `ok`
}
foo.prop = `ng` // Error


Classプロパティも同様でreadonly修飾子をつけたものは変更ができない

class Foo {
readonly prop = `ok`
constructor() {
this.prop = `ng` // ng
}
}


また、Readonly型も用意されておりReadonly型にキャストすることで変更不可にすることもできる。

type Foo = {
prop: string
}
type FooReadonly = Readonly<Foo>
const foo: FooReadonly = {
prop: `ok`
}
foo.prop = `ng` // Error


参考: Readonly · TypeScript Deep Dive
posted by ねこまんま at 15:18 | TypeScript | 更新情報をチェックする

2018年12月14日

FirebaseとNuxt.jsを使ってユーザ認証関係

via: FirebaseとNuxt.jsを使ってユーザ認証関係を簡単に作ってみる

経験が少ない技術のキャッチアップがてらにサンプルをひたすら写経シリーズ。

初期設定



npx create-nuxt-app nuxt_firebase


でインストールして以下のコマンドで実行

cd nuxt_firebase
npm run dev


これで http://localhost:3000/ にアクセスできます。楽

Firebaseをインストールして

npm install firebase


pluginsの中にfirebase.jsを作成

import firebase from 'firebase'

if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: "API_KEY",
authDomain: "APP_ID.firebaseapp.com",
databaseURL: "https://PROJECT_ID.firebaseio.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "1234567890"
})
}

export default firebase


各種の設定はFirebaseプロジェクトの「ウェブアプリに Firebase を追加」から取得できる。

また、左メニューのAuthenticationよりログイン方法のメール / パスワードを有効にして、サンプルユーザーを作成しておく

ストア設定



export const strict = false

export const state = () => ({
user: null,
})

export const mutations = {
setUser (state, payload) {
state.user = payload
}
}

export const actions = {
setUser ({ commit }, payload) {
commit('setUser', payload)
}
}

export const getters = {
isAuthenticated (state) {
return !!state.user
}
}



ログイン機能




/pages/index.vueを以下のように変更しておく

<template>
<div>
<!-- ログイン中に表示される画面 -->
<div v-if="isAuthenticated">
{{ user.email }}でログイン中です<br>
<button v-on:click="logout">ログアウト</button><br>
<a href="/member-page">メンバーページへ</a>
</div>
<!-- ログインしていない時に表示される画面 -->
<div v-else>
メール<br>
<input type="text" v-model="email"><br>
パスワード<br>
<input type="password" v-model="password"><br>
<button v-on:click="login">ログイン</button>
</div>
</div>
</template>

<script>
import firebase from '~/plugins/firebase'
import { mapActions, mapState, mapGetters } from 'vuex'
export default {
data() {
return {
email: '',
password: ''
}
},
computed: {
...mapState(['user']),
...mapGetters(['isAuthenticated'])
},
mounted() {
firebase.auth().onAuthStateChanged((user) => {
this.setUser(user)
})
},
methods : {
...mapActions(['setUser']),
login() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password)
.then(user => {
// ログインしたら飛ぶページを指定
// this.$router.push("/member-page")
}).catch((error) => {
alert(error)
});
},
logout() {
firebase.auth().signOut()
.then(() => {
this.setUser(null)
}).catch((error) => {
alert(error)
})
}
}
}
</script>


これによりログイン時と未ログイン時に状況を切り替えることができる。

ここまでは参考サイトのほぼ引用。
ここからは独自の内容。

アカウント作成



index.vueにアカウント作成用にページへのURLを追加して

      <button v-on:click="login">ログイン</button>
<p><a href="/register-user">新規会員登録</a></p>
</div>
</div>
</template>


/pages/register-user.vueを以下のように設定する

<template>
<div>
メール<br>
<input type="text" v-model="email"><br>
パスワード<br>
<input type="password" v-model="password"><br>
<p v-if="error_code==='auth/invalid-email'">メールアドレスが不正です</p>
<button v-on:click="registerUser">登録</button>
</div>
</template>

<script>
import firebase from '~/plugins/firebase'
import { mapActions } from 'vuex'
export default {
data() {
return {
email: '',
password: '',
error_code:''
}
},
methods : {
...mapActions(['setUser']),
registerUser() {
firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
this.setUser({
email:this.email,
password:this.password
})
this.$router.push("/")
})
.catch(error =>{
this.error_code = error.code
});
}
}
}
</script>


これでアカウント作成成功時にはストアにアカウント情報を登録してindexページに遷移、入力内容が不正な場合はエラーをヒュウ視することができる
posted by ねこまんま at 11:54 | Firebase | 更新情報をチェックする

2018年11月15日

TypeScriptのPartial

Partialを利用すると既存の型から、項目を部分的に共有できる型を作成できます。

ちなみにPartialは部分的という意味

たとえば以下のような型があり

interface Foo {
a: string
b: string
c: string
}


aかbかcを含む型を作りたい場合は以下のように定義しなくてはいけないですが、

interface PartialFoo {
a?: string
b?: string
c?: string
}


Partialを利用すると以下のように定義できます。

type PartialFoo = Partial<Foo>


ちなみにPartialの実態は以下のように定義をされています。

type Partial<T> = {
[P in keyof T]?: T[P];
};
posted by ねこまんま at 15:42 | TypeScript | 更新情報をチェックする

2018年11月12日

Jest + enzyme でイベントをシュミレート

Jest + enzyme でイベントをシュミレートをする際によく書く記述をまとめてみました。

describe('コンポーネントのテスト', () => {

it('関数実行されているか', () => {
const mockFun = jest.fn()

const wrapper = shallow(
<MyComonent onChange={mockFun}/>
)

wrapper.find('button').simulate('click');

expect(mockFun).toHaveBeenCalled();

})

it('関数2回実行されているか', () => {
const mockFun = jest.fn()

const wrapper = shallow(
<MyComonent onChange={mockFun}/>
)

wrapper.find('button').simulate('click');

wrapper.find('button').simulate('click');
expect(mockFun)toHaveBeenCalledTimes(2)
})

it('Stateが変わっているか', () => {
const wrapper = shallow(
<MyComonent onChange={mockFun}/>
)

wrapper.find('button').simulate('click');

expect(wrapper.state('clicked')).toBe(true)

})

it('イベントオブジェクトをシュミレート', () => {
const myText = 'dummy text'

const wrapper = shallow(
<MyComonent onChange={mockFun}/>
)

wrapper.find('input').simulate('change',{
currentTarget: {
value: myText
}
});

expect(wrapper.state('text')).toBe(myText)

})

it('関数が特定の引数で実行されているか, () => {
const myText = 'dummy text'

const wrapper = shallow(
<MyComonent onChange={mockFun}/>
)

wrapper.find('input').simulate('change',{
currentTarget: {
value: myText
}
});

expect(wrapper).toHaveBeenCalledWith({
text:myText
})

})

})
タグ:REACT jest ENZYME
posted by ねこまんま at 15:31 | test | 更新情報をチェックする

2018年11月04日

TypeScriptのPick

TypeScriptのPickを利用すると特定の型から特定の項目を抜き出した型を作成することができます。

export enum Gender {
man,
woman,
etc
}

export interface PersonData {
name: string
age: number
gender: Gender
}

const personData: PersonData = {
name: 'yamada tarou',
age: 12,
gender: Gender.man
}


PersonDataにはnameとageとgenderという3つのプロパティが存在しますが、以下のようにPickを利用してnameとageの型を作成できます。

export type SimpleData = Pick<PersonData, 'name' | 'age'>

export const simpleData: SimpleData = {
name: 'yamada tarou',
age: 12
}


export interface CompanyData {
companyName: string
address: string
tell: string
}

型作成時に&でつなげることで複数の型を連結した型を作成することもできます。

export type StaffSimpleData = Pick &
Pick

export const staffSimpleData: StaffSimpleData = {
name: 'yamada tarou',
age: 12,
companyName: 'toyota',
address: '名古屋'
}


特定の型を追加して行くことも可能です。

export type StaffData = Pick &
Pick & {
staffId: number
}

export const StaffData: StaffData = {
name: 'yamada tarou',
age: 12,
companyName: 'toyota',
address: '名古屋',
staffId: 100
}
posted by ねこまんま at 15:24 | TypeScript | 更新情報をチェックする

2018年09月03日

Jest + enzyme でテストを書く

Jest + enzyme でテストを書いていく

まずはJestのインストール

npm install --save-dev jest


npm install --save-dev babel-jest babel-core regenerator-runtime


sum.jsを以下のようにして

export default sum = (a, b) => a + b


同階層にsum.test.jsを配置

import sum  from './sum'

test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
})


expectで評価したい値を、toBe(X)で値をどう評価したいかを記述する
posted by ねこまんま at 09:58 | test | 更新情報をチェックする

2018年06月30日

TypeScriptのジェネリック

TypeScirptのジェネリックは以下のように関数実行時に型を定義すると関数内で型を利用できる。

function sample1<T>(arg: T): T {
return arg
}
sample1<string>('string')
sample1<number>(1)


型推論が行われるため必ずしも実行時に明示的に型を指定する必要はない

sample1('string')
sample1(1)


関数式の場合は上記のように定義できるけど、関数リテラルの場合か以下のように定義します。

const sample2: { <T>(arg: T): T } = arg => {
return arg
}
sample2<string>('string')
sample2<number>(1)


interfaceを利用して以下のように定義することもできる

interface MyFuncParam<T> {
(arg: string): T
}
interface MyFuncResponse {
type: string
text: string
}
const sample3: MyFuncParam<MyFuncResponse> = text => {
return {
type: 'aaaa',
text
}
}
sample3('foo')
posted by ねこまんま at 01:54 | Comment(0) | TypeScript | 更新情報をチェックする

2018年06月22日

[ts] 'React' は UMD グローバルを参照していますが、現在のファイルはモジュールです。代わりにインポートを追加することを考慮してください。

TypeScript と React で悪戦苦闘していると「[ts] 'React' は UMD グローバルを参照していますが、現在のファイルはモジュールです。代わりにインポートを追加することを考慮してください。」とのエラーが

import { Component } from 'react'


を以下のように変更すると直りました。

import React, { Component } from 'react'
posted by ねこまんま at 12:02 | Comment(0) | React | 更新情報をチェックする

2018年06月13日

Reactでasyncコンポーネントを作成して非同期で読み込みを行う

以下のようなコンポーネントをAsyncComponent.jsなどとして作成
async / awaitを利用して呼び出し時に引数で指定されたコンポーネントの呼び出しを行います。

import React, { Component } from "react";

export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);

this.state = {
component: null
};
}

async componentDidMount() {
const { default: component } = await importComponent();

this.setState({
component: component
});
}

render() {
const C = this.state.component;

return C ? <C {...this.props} /> : null;
}
}

return AsyncComponent;
}


ルーティング時に呼び出したいコンポーネントを利用できるようにしておきます

render(
<div>
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Switch>
<Route
exact
path="/a"
component={asyncComponent(() =>
import('./App1')
)}
/>
<Route
path="/b"
component={asyncComponent(() =>
import('./App2')
)}
/>
<Route
path="/c"
component={asyncComponent(() =>
import('./App3')
)}
/>
<Redirect to="/a" />
</Switch>
</div>
</ConnectedRouter>
</Provider>
</div>,
document.getElementById('root')
);


via : https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
タグ:async REACT await
posted by ねこまんま at 12:38 | Comment(0) | React | 更新情報をチェックする

Reactのreact-helmet-asyncでhead情報を挿入

react-helmet-asyncを利用するとhead内に様々な情報を挿入できる


import * as React from "react";
import { render } from "react-dom";
import Helmet, { HelmetProvider } from "react-helmet-async";

import Hello from "./Hello";

const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};

const App = () => (
<HelmetProvider>
<div style={styles}>
<Helmet>
<title>Hello World!!</title>
<link rel="canonical" href="https://www.tacobell.com/" />
</Helmet>
<Hello name="CodeSandbox" />
<h2>Start editing to see some magic happen {"\u2728"}</h2>
</div>
</HelmetProvider>
);

render(<App />, document.getElementById("root"));


HelmetProviderで包んでおけばHelmetで包んだ要素内でhead要素に入れることができる
posted by ねこまんま at 01:08 | Comment(0) | React | 更新情報をチェックする

TypeScript + Reactの基本

Propsを定義


iterface Props {
value: number;
}
class Square extends React.Component {}


PropsとStateを定義


iterface Props {
value: number;
}
interface State {
value: number | null;
}
class Square extends React.Component {}

タグ:REACT TypeScript
posted by ねこまんま at 00:59 | Comment(0) | React | 更新情報をチェックする

2018年05月17日

React + flow +ESLint + Prettierを試す

React + flow +ESLintを試す の続き。

まずは以下の拡張機能をインストール

yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier


VSCodeのセッティングに以下のコードを追加

  "editor.formatOnSave": true,
"prettier.eslintIntegration": true,


.eslintrcを以下のように変更してあげるとよい

{
"parser": "babel-eslint",
"extends": ["react-app", "standard", "plugin:prettier/recommended"],
"rules": {
"prettier/prettier": [
"error",
{
"trailingComma": "es5",
"singleQuote": true,
"semi": false
}
]
}
}
posted by ねこまんま at 15:54 | Comment(0) | React | 更新情報をチェックする

React + flow +ESLint を試す

React + flow を試すの続き。

ESLintを追加でインストール、色々インストール

yarn add --dev eslint eslint-config-react-app eslint-plugin-flowtype eslint-plugin-jsx-a11y eslint-plugin-react babel-eslint eslint-plugin-node eslint-plugin-promise eslint-config-standard eslint-plugin-standard


.eslintrcを次のように設定して

{
"parser": "babel-eslint",
"extends": ["react-app","standard"]
}


以下のコマンドでエラーで

npx eslint src/App.js


以下のようなエラーが出ればOK

Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash


これは先程インストールしたjsx-a11y内のhref-no-hashというプラグインのhref-no-hashの名前が変わっているため。以下のようにして古いバージョンを入れることで直すことができます。

yarn remove eslint-plugin-jsx-a11y
yarn add --dev eslint-plugin-jsx-a11y@^5.1.1
posted by ねこまんま at 15:07 | Comment(0) | React | 更新情報をチェックする

React + flow を試す

React + flow を試す。

まずはcreate-react-appでプロジェクトをサクッと作成

create-react-app flow-test
cd flow-test
yarn start


次にflow-binをインストールして.flowconfigを作成する

yarn add --dev flow-bin
yarn run flow init


Visual Studio CodeにはプラグインFlow Language Support」をインストールしておきプロジェクトの設定を以下のようにしておく

{    
"flow.useNPMPackagedFlow": true,
"javascript.validate.enable": false
}


App.jsの冒頭に「// @flow」を追加する

以下のようにエラーがでて怒られますので

[flow] Cannot use `Component` [1] with less than 1 type argument. (References: [1])



以下のようにComponentに型を定義してあげるとエラーが無くなります。

type Props = {};
class App extends Component {




posted by ねこまんま at 12:10 | Comment(0) | React | 更新情報をチェックする

2018年05月05日

ReactでCSS Modulesを試す

すでに、Webpackで動いているReactプロジェクトに導入を前提としています。

まずは関連システムをインストール。

npm install --save-dev style-loader css-loader sass-loader node-sass extract-text-webpack-plugin


webpack.config.jsに会の一文を追加

module: {
rules: [
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader?modules', 'sass-loader'],
},
]
},


app.scssなどのファイルを作成して、以下のような内容を指定

.base{
background: blue;
}



app.js内でCSSを読み込み


import styles from './app.scss';



<div className={styles.base}>...</div>


などとして指定が可能
タグ:REACT CSS Modules
posted by ねこまんま at 17:34 | Comment(0) | React | 更新情報をチェックする

2018年04月12日

JEST入門

簡単なコードを買いてコレをテストしていく。
とりあえずCreateReactAppのものを使うので環境構築はなし。

xxx.jsを作成して

export default (a,b) => a + b;


xxx.test.jsとしてテストファイルが作成できるので以下のようにテストコードを書く

import sum from './sum';
test('sum',()=>{
expect(sum(2,3)).toBe(5);
})


`npm test` でテストが実行される

posted by ねこまんま at 20:15 | Comment(0) | test | 更新情報をチェックする