関数の引数にスプレッド構文を使った場合
背景
React書いていると以下のような...props
のような記述がたまに見られるが、どういう振る舞いになるのか少し混乱したから整理する。
const DeleteButton = ({className, ...props}) => ( <span className={[style.root, className].join(' ')} {...props}> <TrashCanIcon /> <Baloon>削除する</Baloon> </span> );
スプレッド構文
ドットを3つつなげたこれはスプレッド構文と呼ばれている。Iterableなオブジェクトの中身を展開してくれるようだ。
これらは以下の3つの場面で使うことができる。 1. 関数呼び出し 2. Arrayリテラル 3. Objectリテラル
2,3は想像が付くんだが関数呼び出しのときの振る舞いがうまく想像できない。
実験
とりあえず使ってみる。
const hage = ({...props}) => {console.log(props)}; hage({a:1}); // => {a: 1}
const hage=(props) => {console.log(props)};
と変わらないっぽい?
propsの中身を一度展開し、再度Objectにしたものをprops
という名前で束縛しているのか?
そもそも({a, b}) => {}
って引数に渡されたObjectからa
とb
のプロパティだけを抽出する構文だよね?なんかおかしくね?
const hige = ({...props}) => {console.log(a)}; hige({a: 1}); // => ReferenceError: a is not defined
これでa
に1
が束縛されるんじゃないかと思ったんだけどな…。
propsの中身を一度展開し、再度Objectにしたものを
props
という名前で束縛している?
これが正解っぽい
const huge = ({a, ...props}) => {console.log(a, props)}; huge({a: 1, b: 2, c: 3}); /// => (1, {b: 2, c: 3})
なるほど?...を付けたら問答無用で残りは引き渡ってくれるのかー。
結論
というわけで最初の例はなんでもごちゃごちゃに渡されてくるprops
からclassName
だけをチュ出し、残りの要素は再度props
に束縛するという感じか。納得はしてないが振る舞いはわかった。
再掲
const DeleteButton = ({className, ...props}) => ( <span className={[style.root, className].join(' ')} {...props}> <TrashCanIcon /> <Baloon>削除する</Baloon> </span> );
storybookに階層構造を追加する
背景
デフォルトでは1階層しかパスが切れない。
例えばAtomsのButton、MoleculesのFormがあったとして、理想的には以下のように整理したい。
- Atoms
- Button
- with text
- with some emoji
- Button
- Molecules
- Form
- ContactForm
- MailAddressForm
- Form
しかしデフォルトでは以下のようにするしかない。
- Button
- with text
- with some emoji
- Form
- ContactForm
- MailAddressForm
@storybook/addon-chaptersを使うと実現できるらしい。おれは以下の本を読んでこのアドオンの存在を知った。
Atomic Design ~堅牢で使いやすいUIを効率良く設計する | 五藤 佑典 | コンピュータ・IT | Kindleストア | Amazon
公式のaddon galleryでも紹介されている。
https://storybook.js.org/addons/addon-gallery/#chapters
階層構造と読んでいたがChapterと言うようだ。
@storybook/addon-chapters
install
npmを見る。
@storybook/addon-chapters - npm
storiesOf('Atoms', module) .addChapter('Button', chapter=>chapter .add('with text', () => <Button>with text</Button>) .add('with som emoji', () => <Button>with some emoji</Button>) ); storiesOf('Molecules', module) .addChapter('Form', chapter=>chapter .add( ...
というような感じで階層構造を追加出来た。
storybook-directory-chapters
components以下のdirectory構造をそのままstorybookにできるnpmパッケージもあったので試してみたが動かないようだ。 storybook-directory-chapters - npm
設定で module.exports
を使っているがimport
文も使っておりEJSとCJSの仕様がごちゃまぜになってる。module.exports
をexports default
に書き換えたが今度はcontext(...) is not a function
というエラーが出てしまい解消できなかった。
formikを使ってみる
前提
$ node --version v.9.11.1
React, ReduxでSPAを作っている。
背景
connectで渡されたpropsをComponentで加工してFormを作っている場合、Formを触って変更された値をStoreまで持っていっても、その値はあくまでもFormのみで有効な値であるから、Storeに反映させる変更は無いことに気付いた。
例えば、read・write・ execの3つの権限をチェックボックスで付けたり外したりできるFormを考えるとわかりやすい。connectでは3つの権限のうち、既に付いている権限だけが ["read", "exec"]
のように配列で渡されてくる。Componentではこれを {read: true, write: false, exec: true}
のような辞書に変換してチェックボックスを作る。ここからが重要で、チェックボックスをクリックすると onChange
に登録されたイベントハンドラには {read: false}
のような辞書が引数として渡されることになる。しかし、これをStoreまで引きずっていってもどうしようもないのである。Storeが欲しいものはread・write・execの全ての状態がどうなったかという、Formで最終的に生成された状態であって、「readがfalseになりました」というような断片的な情報には興味が無い。もちろん {read: false}
を受け取って ["read", exec"]
を ["exec"]
に変更することはできる。しかし、今回はたまたま簡単にハンドリングできるだけであって、より複雑なFormでは現実的でないだろう。
解消方法としては2つ思いついた
- StoreにFormごとのstateを持たせる
- reactのstateを使う
1は論外だろう。あり得ない。というわけで2番を採用する方向でReduxを利用しているときのReactのstateの利用有無について調べていたら、どうやらこれがReact界隈でいうところの Global State vs Local State という議論らしい。
ReduxにおけるGlobal stateとLocal stateの共存 - LIVESENSE ENGINEER BLOG
まあ現実的にはLocal Stateを解禁するしかないだろうと思ったが、一応社内のフロントエンジニアに聞きに行ったら今は「Ephemeral Stateという概念があるよ」と言ってFromikを教えてくれた。
GitHub - jaredpalmer/formik: Build forms in React, without the tears 😭
3行でどういうものか説明して、と頼んだらこういうものだと言う。
- formのような一時的なstate管理はreduxで管理しなくていいよ
- 代わりにpropsをformのstateにmappingしてくれる機能を用意したよ
- formを送信するタイミングでformのstateをreduxに流し込むよ
つまり、ComponentのstateでもなくStoreのstateでもない中間層を用意してそこにFormの中間状態を保持してsubmitのタイミングでStoreに渡してくれるようだ。
欲しかったものっぽいので試してみる。
BasicForm
公式のデモのBasicがシンプルなフォームを作っていてわかりやすかったので、適当に不要な所を省いて写経した。
formik-tutorial/index.js at master · roronya/formik-tutorial · GitHub
使ってみた感じは彼が教えてくれた3行そのままだった。その他に気付いたことは、handleChange
やhandleReset
など良く使うハンドラは既に定義されていて一般的な振る舞いをやってくれる。また、特殊な動きがしたければ簡単に上書きも可能というもののようだった。中間状態はFormikの定義しているvalues
が担うようだ。
背景で説明したread・write・execのFormを実装するならば、wirhFormikの mapPropsToValues
で配列から辞書に変換して、handleSubmit
でvalues
から辞書を取り出して配列に戻してActionCreatorを呼び出すようになるかなと思った。
PermissionSelectorForm
背景に書いた、read・write・execのFormにFormikを適用してみたい。このFormをPermissionSelectorFormと呼称することにする。
Reactだけで実装
多分こんな感じになる。
formik-tutorial/index.js at master · roronya/formik-tutorial · GitHub
Formikを適用する
多分こうなる。
formik-tutorial/index.js at master · roronya/formik-tutorial · GitHub
ハマりどころ
handleChangeかsetFieldValueか
最初はinputのonChange
にhandleChange
を渡していたが動かなかった。handleChange
は<input type=text>
のような逐次変わる値を追従させるときに使うようだ。<input type=checkbox>
みたいなやつはsetFieldValue
を使う。
また、setFieldValue
の第一引数は厄介だった。withFormic
のmapPropsToValues
で作っているprops
の構造を文字列で書く必要がある。props
の構造を複雑にしていると大変だ。
setFieldValueの使い方を良くよむこと。
GitHubのissueにcheckbox使う場合の例がcodesandboxで置いてくれているのが参考になった。 How to populate array with checkboxes? · Issue #360 · jaredpalmer/formik · GitHub
上書きできるのはhandleSubmitだけ?
↑でハマっていたときにhandleChange
をwithFormic
で上書きしてしまえばいいのでは?と思ってためしたができなかった。上書きできるのはhandleSubmit
だけらしい。
というわけでFormikでやりたいことができていい感じだった。おすすめ。
prettierがいい感じ
背景
WebStormのフォーマッターがいけてない。例えば
import React from 'react'
としたら
import React from 'react';
とセミコロン付けて欲しいが付けてくれない。 ほかにもダブルクオーテーションとクォーテーションを統一してくれない、とか、細かい不満がいっぱいあった。
Prettier
適当にググったらこれが出てきた。
https://qiita.com/furuk4wa/items/5f5a28e81a15653af0c8
名前だけ分かれば後は公式を見る。
install
$ yarn add prettier --dev --exact
https://prettier.io/docs/en/install.html
WebStormとの連携
公式に説明がある。
https://prettier.io/docs/en/webstorm.html
WebStormのバージョンが2018.1以上なら Alt-Shift-Cmd-P でフォーマットがかかるらしい。確かにかかった。上の ;
の補完もやってくれる。クォーテーションの統一もしてくれる。いい感じ。
1028.07.12 追記
Alt-Shift-Cmd-Pを押しても何もおこらないとき
prettierの実行ファイルへのパスを指定しないとダメみたい。↓にやり方が書いてある。
JavascriptのExportについて
前提
$ node --version v.9.11.1
背景
import React from 'react'; import {render} from 'react-dom';
前者のように{}無しで書く場合と後者のように{}有りで書く場合があって、よくわからなかったから調べる。
名前付きエクスポートとデフォルトエクスポート
エクスポートの方法によってインポートの方法も変わるようだ。
名前を付けてエクスポートする場合はインポート側もその名前で束縛しなければならない。デフォルトエクスポートの場合はインポート側で束縛する名前を指定できる。
以下はlib.jsでaとbを定義してindex.jsでaをxとして、bをbとしてインポートしている。
lib.js
const a = 'this is a'; const b = 'this is b'; export default a; export {b};
index.js
import x from './lib'; import {b} from './lib'; console.log(x); console.log(b);
参考
名前付きエクスポートとデフォルトエクスポートについてはMDNを良く読む。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export
nodeでimport/exportを使う方法
ついでにnodeでimport/export文を使うには .js
でなく .mjs
として保存し、 node --experimental-modules index.mjs
と実行する。
参考
適当にググってこのQiitaが出てきた。
https://qiita.com/ryohji/items/93f5050b9af6fc15693c
公式はこのへん。
dockerの-vオプション
なんかいつも忘れるからメモ
書き方
docker -v ホストのディレクトリ:コンテナのディレクトリ image名
カレントディレクトリをマウントしたいとき
docker -v $(pwd):/path/to/directory image
覚え方
前と後ろのどっちがホストのディレクトリだっけ?っていつも悩んでしまう。
Dockerは基本的に基本的にホスト:コンテナの順で書く。だから大抵source => destinationの順になってる。
-v $(pwd):/path/to/directory
だったらホストのカレントディレクトリをコンテナの /path/to/directory
にマウントする。
portもそう。-p 80:8080
だったらホストの80番をコンテナの8080にフォワードする。
展開のされかた
以下のようなディレクトリ構造がホストに構築されているとする
- /app/build/
- index.html
- style.css
で、-v /app/build:/usr/share/nginx/html
と書くとコンテナでは以下のように展開される。
- /usr/share/nginx/html/
- index.html
- style.css
以下のようにはならない。
- /usr/share/nginx/html/
- build/
- index.html
- style.css
- build/
逆に↑のようにしたければ以下のようにコンテナのパスの方に書くしか無い。
-v /app/build:/usr/share/nginx/html/build
またコンテナのパスのほうに存在しないディレクトリを書いても勝手に作ってくれる。
TAMRON 28-75mm F/2.8 Di III RXD
おれのα7にはオートフォーカスするレンズが着いたことが一度もない。初代α7が発売されてすぐに買ったから、もう4年間も使っていることになるが、全てフルマニュアルで撮ってきた。カメラマンだったらしい祖父の遺した古いNikkorとCanon FDレンズだけがレンズ資産だった。レンズがそんなだから、ピントも絞りもレンズを直接いじくって決めるし、「どうせなら」と思って測光もマニュアルでやっていた。撮影には手間と時間がかかるけど、それくらい時間をかけられるような余裕を持って被写体を眺めた方が発見があるんじゃないかなと思っている。
この桜の写真は AI Nikkor 50mm f/1.4Sで撮った。風が吹くとすごい量の花びらが舞うので、その様子を撮りたくて、1時間くらい待った。それくらいの時間感覚で写真とは向き合うのがおれは楽しいと思っている。
しかし、この時間のかけ方は趣味で一人でやるにはいいけど、人と旅行に行くときはそうもいかないので、やはりオートフォーカスするレンズが欲しいなあと去年頃から実は思っていた。それで、今年のCP+でTAMRONがEマウントのフルサイズ向けに標準ズームを出すと発表していたから、発売したら買おうと思っていた。
それがこれ。今日はこれを持って旧古河庭園に行った。
解像度が高い。おれは今までα7の本気を知らなかったんだなあと思った。洋館の壁面の写真なんか、構図も意識せずに適当にパシャッとやったんだけど、それなりの写真に見えてしまう。被写体の質感を正確に伝えるだけで表現になるというのは知らなかった。1枚目も3枚目も木々の葉の質感だけで訴えられる。
この下の桜の写真はさっきの桜の写真と同じレンズで撮ってるんだけど、上に比べるとまったく解像してなかったんだとわかる。撮ったときは結構撮れてるじゃんと思ったんだけどな。
とにかく解像度に驚いた。しかしなんでも解像すれば良いというものでもないだろうとも思う。表現の幅としてどれくらい解像させるか?という軸が自分の中に加わった。