VSCode で Prettier の設定が無視されて効かない問題
tl;dr
./.prettierrc.json
を作成する。
Prettier の設定が無視される
VSCode の formatOnSave
機能で Prettier の設定が効きませんでした。
Prettier 設定を workspace で行ったり、.prettierrc
に書いたりしましたが、どちらも VSCode から無視される状態。
issue
prettier-vscode レポジトリにてこんな issue comment を発見。
.prettierrc.json works. prettierrc.js and prettierrc.config.js do not work.
https://github.com/prettier/prettier-vscode/issues/371#issuecomment-447757781
.prettierrc.json
なら動くとのこと。試したところ、自分の環境では問題が解決しました。
【JavaScript, VSCode, Prettier】import 文を自動改行
td;dr
VSCode で Prettier の設定を行い、 import 文を自動改行する。
概要
ESLint/TSLint によって1行のコードの長さを制限することができます。
- ESLint: max-len
- TSLin: max-line-length
import 文もこの対象になりますが、auto fix が効かないので手動で修正することになり、やや面倒です。
これを自動修正するには、 Prettier を使います。
VSCode の自動フォーマット設定
⌘+,
で設定画面を開き、prettier の自動フォーマットが効くようにします。
自分はファイル保存時にフォーマットしてほしいので、 editor.formatOnSave
を有効にしました。
他には、ペースト時にフォーマット(editor.formatOnPaste
)、タイピングごとにフォーマット(editor.formatOnType
)もありますのでお好みで。
ちなみに、ESLint/TSLint を利用している場合は、以下の記事も参照ください。
VSCodeにおける自動フォーマットで整形が崩れる問題への対処法 - qiita.com
VSCode Prettier パッケージ
prettier-vscode をインストールします。
Prettier の改行行数を設定
プロジェクトルートに .prettierrc.json
ファイルを作成し、 printWidth
設定を追加します。
{ "printWidth": 140 }
これで、140字を超える行数に関しては、改行が挿入されるようになります。
ちなみに、.prettierrc
や prettierrc.js
ではなく .prettierrc.json
を使った理由については以下も参照ください。
VSCode で Prettier の設定が無視されて効かない問題 - chuckwebtips.hatenablog.com
VSCode で行番号カラムの幅を狭める
【JavaScript】Vue.js で retweet / link リンク【Twitter】
概要
ツイートを埋め込むような web サービスを作る場合、リツイートとイイネのリンクを実装することがあるかもしれません。
そのリンク(正式名称はインテント)を Vue.js で作成する方法です。
実装
リンクの定義です。
// ツイート ID (いわゆる id_str) const tweetId = "foo" // イイネ インテント リンク const likeUrl = `https://twitter.com/intent/like?tweet_id=${tweetId}` // リツイート インテント リンク const retweetUrl = `https://twitter.com/intent/retweet?tweet_id=${tweetId}`
マークアップです。
/* イイネ インテント */ <a :href="likeUrl"> /* お好みでアイコンなど */ </a> /* リツイート インテント */ <a :href="retweetUrl"> /* お好みでアイコンなど */ </a>
上記の a タグをクリックすると、以下のようなリツイート確認画面が開きます。

参考 URL
公式ドキュメント: Web Intents - developer.twitter.com
【Node.js】Mongoose の upsert で、 ドキュメント作成時のみ追加するフィールド
Mongoose の upsert
便利ですよね。正しくは、upsert: true
オプションですね。ドキュメントが存在しなければ作成、存在すれば更新をしてくれます。
参考: How do I update/upsert a document in Mongoose? - stackoverflow.com
さてそんな upsert を行う時、作成時のみに何かしたいというケースがあります。例えば、 created_at
のようなフィールドの作成でしょうか。一度作成されたら更新されたくない、といったフィールドです。
そんな時、 $setOnInsert
が便利です。
以下、サンプルコードです。
import mongoose from '../libs/mongoose'; const { Schema } = mongoose; // schema const ThingSchema = new Schema({ id: Number, name: String, created_at: Date, updated_at: Date, }); const Thing = mongoose.model('Thing', ThingSchema); Thing.upsertThing = (thingId, thingName) => Thing.findOneAndUpdate( // ドキュメント検索条件 { id: thingId }, // ドキュメントデータ { // 常に適用される $set: { id: thingId, name: thingName, updated_at: new Date(), }, // 作成時にのみ適用される $setOnInsert: { created_at: new Date(), }, }, // オプション { new: true, upsert: true }, )
$setOnInsert - docs.mongodb.com
If an update operation with upsert: true results in an insert of a document, then $setOnInsert assigns the specified values to the fields in the document. If the update operation does not result in an insert, $setOnInsert does nothing.
【JavaScript】Google Optimize を React アプリケーションで使う
Google Optimize を React アプリケーションで使う
概要
AB テストツールである Google Optimize を React.js で作るアプリケーションで使う。
React アプリケーション作成
簡単に React アプリケーション を作成できる create-react-app を使う。
$ npx create-react-app APPLICATION_NAME $ cd APPLICATION_NAME $ npm start
APPLICATION_NAME
は何でもよい。
Optimize
アカウントが未作成の場合、 Google Optimize にて作成する。
アカウントを作成したら、コンテナを1つ作成し、名前をつける。
コンテナを作成したら、エクスペリエンスを作成する。
名前: My first experience エディタページ: http://localhost.domain:3000
とする。名前は何でも良い。 Optimize はローカル環境で動かないので、hosts ファイルに編集を加える。
# /etc/hosts 127.0.0.1 localhost.domain
なぜ、ローカル環境で Optimize が動かないかの理由はこちら。
エクスペリエンスを作成したら、パターン(variant) を作成する。
パターンのデフォルト名は パターン 1
。
パターンを作成したら、Google Analytic のプロパティと連携する。
連携ボタンは↑のような感じ。(2019/07/30時点)
連携が完了すると、以下のスニペットがモーダル表示される。
<!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=GA_PROPERTY_ID></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'GA_PROPERTY_ID', { 'optimize_id': 'OPTIMIZE_ID'}); </script>
各 ID はぼかして書いていることに留意。
コードベースに戻り、上記のスニペットを index.html
の <head />
内に貼り付ける。
Google Optimize のダッシュボードに戻り、メインの目標をページビュー数として設定する。
「設定」の項目までスクロールダウンして、「オプティマイズのインストール」を実行する。 この時、Chrome 拡張のインストールをオススメされる。
「オプティマイズのインストール」が成功すると、遷移先にて以下のようなモーダルが表示される。
エクスペリエンスを実行する
ダッシュボードにて、「パターン1」を編集する。
編集ボタンを押すと、localhost.domain:3000 が表示され、編集可能状態に入る。
UI 要素に hover すると、以下のように選択される。
p タグ内のテキストを Google Optimize on React!
のように編集する。
編集が完了したら、保存を選択して、ダッシュボードに戻る。
エクスペリエンスの実行準備が整ったので、「開始」ボタンを押す。 シークレットブラウザで localhost.domain:3000 を開くと、ABテストが実行されていることが確認できる。
自前実装する場合
これまでは、Google Optimize のダッシュボードからエクスペリエンスを作成した。
より複雑なテストには自前で実装することもできる。
エクスペリエンスを新規作成し、ダッシュボードの最下部にある「アクティベーションイベント」を選択。 すると、右側のモーダルメニューとして「評価するイベント」を選択することができる。
これを「カスタムイベント」に設定し、エクスペリエンスを開始する。
すると、コード側からパターン情報を取得できる。
async componentDidMount() { if (window.dataLayer) { await window.dataLayer.push({ event: "optimize.activate" }); } this.intervalId = setInterval(() => { if (window.google_optimize !== undefined) { const variant = window.google_optimize.get("EXPERIENCE_ID"); this.setState({ variant }); clearInterval(this.intervalId); } }, 200); }
variant
には 1
や 0
と言ったデータが渡ってくる。
参考URL
How to Add Google Optimize A/B Testing to Your React App in 10 Lines of Code - medium.com
【JavaScript】forEach と for文 における配列イテレーション差異
forEach と for文 における配列イテレーション差異
解説
JavaScript において、 forEach と for 文の違いはいくつかあると思うのですが、今回は配列イテレーションの差異について書きます。
new Array(n) のような形で配列を作成すると、empty item が初期値として設定されます。
// 要素数10の配列を新規作成 const ary = new Array(10) // 0番目と3番目の要素に文字列を設定 ary[0] = "foo" ary[3] = "bar"
> console.log(ary) [ 'foo', <2 empty items>, 'bar', <6 empty items> ]
この状態の配列をイテレーションすると、for文とforEachにおいて挙動に差異が生まれます。
for文が empty item まで含めてイテレーションするのに対して、
// 10回標準出力される for (let i = 0; i < ary.length; i++) { console.log(ary[i]) } // foo // undefined // undefined // bar // undefined // undefined // undefined // undefined // undefined // undefined // undefined
forEach では empty item を除いた要素がイテレーションされます。
// 2回照準出力される ary.forEach((item) => { console.log(item) }) // foo // bar
URLs
自分用 CSS Grid チートシート
CSS グリッドレイアウトに関するチートシートです。自分用なので結構雑です。
用語
Grid container
グリッドコンテナ。グリッドレイアウトを敷くもの。
Grid item
グリッドコンテナに包含されるアイテム。
Grid column
レイアウト上の列。
Grid row
レイアウト上の行。
Grid track
グリッドトラック。レイアウト上の縦線と横線のこと。 4x4なレイアウトなら、+ のようなトラックが引かれる。
Grid area
グリッドエリア。任意のグリッドトラックに囲まれた領域。
Grid cell
グリッドセル。グリッドトラックをまたぐこともあるグリッドエリアとは異なり、単一のグリッド領域を指す。
プロパティ
コンテナ系
display
Grid レイアウトを使うために指定してなくてはいけないもの。
.container { // ブロックレベルのグリッドコンテナを敷く。 display: grid; // インラインレベルのグリッドコンテナを敷く。 display: inline-grid: }
grid-template-columns, grid-template-rows
それぞれ、行と列のサイズを指定する。
.container { // 1列目が60px, 2列目が80pxになる grid-template-columns: 60px 80px; // 1行目が40px, 2行目が4em, 3行目が40pxになる grid-template-rows: 40px 4em 40px; }
grid-template-area
名称付きグリッドエリアを指定する。
.container { // 左上4マスを"a", 中央下1マスを"b", 右下1マスを"c" と指定 grid-template-areas: "a a ." "a a ." ". b c"; }
grid 系の他のプロパティから利用可能。
grid-template
grid-template-columns, grid-template-rows, grid-template-areas のショートハンド。
.container { grid-template: "a a ." minmax(50px, auto) "a a ." 80px "b b c" auto / 2em 3em auto; }
grid-column-gap, grid-row-gap
水平と垂直のグリッドトラックの幅を指定。
.container { // 列グリッドトラックの幅を10pxにする grid-column-gap: 10px; // 行グリッドトラックの幅を10pxにする grid-row-gap: 15px; }
grid-gap
grid-column-gap, grid-row-gap のショートハンド。
.container { // グリッドトラックの幅を、列15px, 行10px で指定 grid-gap: 15px 10px; }
justify-items
グリッドアイテムの水平方向の寄せ方を指定。
.container { // 中央寄せ justify-items: center; // 始点寄せ(始点が左なら左寄せ) justify-items: start; // 終点寄せ(終点が右なら右寄せ) justify-items: end; // 他にも色々ある }
align-items
グリッドアイテムの垂直方向の寄せ方を指定。
.container { // 中央寄せ align-items: center; // 始点寄せ(始点が左なら左寄せ) align-items: start; // 終点寄せ(終点が右なら右寄せ) align-items: end; // 他にも色々ある }
place-items
justify-items と align-items のショートハンド。
.container { // 垂直方向には中央寄せ、水平方向には終点寄せ place-items: center end; // 両方中央寄せ place-items: center; }
justify-content
グリッドコンテナ自体を水平方向にどのように寄せるか。
.container { // 水平方向に中央寄せ justify-content: center: }
justify-content, align-content, place-content
それぞれ、justify-items, align-items, place-content のグリッドコンテナ版。
grid-auto-columns, grid-auto-rows
(未完)
grid-auto-flow
位置指定のゆるいグリッドアイテムたちをどのように自動敷き詰めするか指定する。
.container { // (グリッドアイテムの順番を保ちつつ)水平方向で敷き詰め grid-auto-flow: row; // (グリッドアイテムの順番を保ちつつ)垂直方向で敷き詰め grid-auto-flow: column; // グリッドアイテムの順番を変えてでも敷き詰め grid-auto-flow: dense; }
アイテム系
grid-column-start, grid-column-end, grid-row-start, grid-row-end
グリッドアイテムを、グリッドレイアウト上のどこに置くか指定する。
grid-column, grid-row
それぞれ、(grid-column-start + grid-column-end), (grid-row-start, grid-row-end) のショートハンド。
grid-area
グリッドアイテムに名前を与える。
.a-item { grid-area: a; // ".a-item" が4マス分のグリッドエリア "a" を占める }
また、grid-column-start, grid-column-end, grid-row-start, grid-row-end のショートハンドでもある。
.item { grid-area: 1 / col4-start / last-line / 6 }
justify-self
グリッドアイテム自身の水平方向の寄せ方を指定。
.item { // 自身をグリッドセル内で中央寄せ justify-self: center; }
align-self
グリッドアイテム自身の垂直方向の寄せ方を指定。
.item { // 自身をグリッドセル内で中央寄せ align-self: center; }
place-self
justify-self, align-self のショートハンド。
.item { // 自身をグリッドセル内で、垂直方向に中央寄せ、水平方向に終点寄せ place-self: center end; }
React v16.6.0で追加された lazy, memo そして contextType を試してみる
React.js v16.6.0 にて追加された新機能を試してみました。
環境づくり
サクッと、create-react-app
を利用しました。
$ npm -g install create-react-app $ npx create-react-app my-app $ cd my-app $ yarn start
初めて create-react-app
を利用したのですが、簡単すぎてビビりました…。React で何か試したい時は積極的に使っていきたいですね。
React.memo
プレゼンテーションコンポーネント(独自に state を保持したり、ライフサイクル関数を使うことなく、描画の行うだけのコンポーネント)を定義するする時は、クラスコンポーネントではなく SFC
を利用しています。
import React from 'react'; // ふつーの SFC const Hello = () => { return ( <div>Hello React v16.6!</div> ) }
しかし、最近業務で引っかかった問題として、SFC で不要な再レンダリングが起こっていました。
再レンダリングの原因は、空配列であった state に空配列を再代入していたことでした。値は変わっていないのに、参照が変わっていたため、SFC が props に更新ありと判定していました。
参考リンク: optimizing-performance - reactjs.org
そこで、PureComponent
への書き換えを行いました。
PureComponent
は props を shallow
比較するのでパフォーマンス向上に寄与しました。
React.memo
を使うと、SFC を PureComponent に書き換える事なく同様のことが実現できます。
参考リンク: React.memo - reactjs.org
import React from 'react'; // ふつーの SFC const Hello = (props) => { return ( <div>Hello React v16.6!</div> ) } // props を shallow 比較するコンポーネント const PureHello = React.memo(Hello)
クラスへの書き換えの手間がかからなくて良さげです。
React.Suspense と React.lazy
ここで、API フェッチを行い、そして待ち合わせを行うようなコンポーネントのケースを考えてみます。 従来ならこのように書いていました。
import React, { Component } from 'react'; // 与えられた Todo を表示する const Todo = (props) => { if(props.isLoading) { return <div>Loading...</div> } return ( <div>{ props.todo.title }</div> ) } // Todo のフェッチを行い、フェッチ状況も管理する class App extends Component { state = { isLoading: false, todo: null } componentDidMount() { this.setState({ isLoading: true }) fetch('https://jsonplaceholder.typicode.com/todos/1') .then(res => res.json()) .then(json => { this.setState({ isLoading: false, todo: json }) }) } render() { return ( <Todo isLoading={this.state.isLoading} todo={this.state.todo} /> ); } }
React.Suspense を使って書くと以下のようになります。(多分、細部では違った処理になっているかもしれません)
import React, { Component, Suspense } from 'react'; const Todo = (props) => { if(todo === null) { throw new Promise((resolve, reject) => { fetch(`https://jsonplaceholder.typicode.com/todos/${props.id}`) .then(res => res.json()) .then(json => { todo = json resolve() }) .catch(e => { console.warn("Not fetched yet") reject() }) }) } return ( <div>{ todo.title }</div> ) } class App extends Component { render() { return ( {/* Suspense にフェッチ完了までのリンダリングを引き受けさせる*/} <Suspense fallback={<div>Loading...</div>}> <Todos id={1} /> </Suspense> ); } }
参考URL: React 16.6で追加されたReact.Suspenseについて - qiita.com
さらに、この Suspense ですが、React.lazy を使った dynamic import
によって Code splitting を噛ませることもできるようです。
import React, { Component, Suspense, lazy } from 'react'; const Todo = lazy(() => import('path/to/Todo.jsx')) class App extends Component { render() { return ( <Suspense fallback={<div>Loading...</div>}> <Todo id={1} /> </Suspense> ); } } export default App;
SSRではまだ使えないようですが…。
参考URL:
static contextType
Context は以前から実装されていた機能ですが(実はあんまり使ったこと無い)、 クラスの static 変数として context にアクセスできるようになったみたいです。
import React, { Component } from 'react'; const MyContext = React.createContext("default value"); class MyClass extends React.Component { static contextType = MyContext; render() { console.log(this.context) // "default value" return ( <div>{ this.context }</div> ) } }
参考URL: static contextType - reactjs.org
static getDerivedStateFromError()
従来の componentDidCatch
に加えて、 getDerivedStateFromError
が追加されました。
componentDidCatch の問題点は、SSR に対応していないことでした。 getDerivedStateFromError は将来的には SSR 対応されるようなので、ユニバーサルなアプリケーションには役立ちそうです。
参考URL: static getDerivedStateFromError()
以上、 React v16.6 で追加された機能について試したり、調べたりしてみました。