Kyoto.js 15 で「CSS in JSでメディアクエリをパッとやる」というトークをした

Kyoto.js 15に参加した。

発表枠で応募してLT枠という形で出た。けど5分じゃなくだいたい10分位というざっくりした感じで喋った。たぶん10分位で喋れてたと思う(計るの忘れてた)。

しゃべった内容

CSS in JSと書いてから気づいたけど主語が大きすぎると思って会場で訂正した……CSS in JSってめっちゃある……

それで僕が想定しているというか動作確認しているのはReactでstyled-componentsとかemotionを使うとき…なので会場ではそう訂正させて頂いた。

なんかよくわからない止まってるスライドは会場で動画でデモを流していたスライド。speakerdeckにアップロードしているPDFには動画埋め込みはできないので割愛ということで🙏

リポジトリです

github.com

npmにもパブリッシュ済みなのでスライドの通り

yarn add @1natsu/handy-media-queryで入ります。

正直v1は完璧は求めずとりあえず作ってみるか〜という気持ちで作っていて圧倒的API不足…widthベースの3つのメソッドしかない。

v2のアップデートでもうちょいメソッド足してうまい具合にできたらな〜と思っている。

感想

Kyoto.jsはじめて参加したけど、楽しい会だった。いい具合にのんびりしていてよかった。

会場のディスプレイでどういう具合にスライドが映って見えるかわからなかったから「これくらいなら見えるかな〜」と思ってスライド作ったけど、実際は想定よりコードが小さく映ってしまって見にくかったのは非常に申し訳なかった。

次回からはもっとコードも大きい文字にしていきたい。

懇親会では発表内容についてReact書いてる人たちから共感の感想をいただけてよかった。「便利そう〜」とか「イレギュラーなメディアクエリわかる〜」とか「テンプレートリテラル内なのでたしかに〜」など。自分の気持ちとアプローチが独りよがりじゃなかった実感が得られて助かりがあった。

あとインターネットのひとと知り合いになれてよかった。

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


会場ではサラッと言いましたがいまほぼ無なのでいいお話や縁談を歓迎しています。

React Hooks使って『Hooks can only be called inside the body of a function component.』って怒られる問題

まだalphaリリース(v16.7.0-alpha)のHooksを使っていたらハマった。

『Hooks can only be called inside the body of a function component.』とエラーバウンダリーの画面で怒られてしまう。

状況

  • 親プロジェクトでHooksを用いたライブラリコンポーネントを作成している
  • 作っているライブラリコンポーネントを子プロジェクトにlinkして子プロジェクト内で試用しようとした

怒られる。

ディレクトリ図はこういう感じで、ライブラリ開発現場(親)の中にexample(子)ディレクトリがあって、子は子で別途package.jsonnode_modulesを抱えているという状況。

.
├── example
│   ├── node_modules
│   ├── package.json
│   └── src
├── node_modules
├── package.json
└── src

なおexampleディレクトリはライブラリのデモページのためのディレクトリ。

この構成は

このcreate-react-libraryというボイラープレートの構成をベースにしている。

Issue

github.com

Dan氏が想定の挙動だよ〜ってコメントしている

エラーの原因はライブラリが参照しているReactと子ディレクトリで参照しているReactが異なるためらしい。

  • ディレクトリで親階層にあるライブラリを利用はするけど作成中のライブラリにLinkを貼っているだけ
    • ライブラリが参照するのは/node_modules/react
  • ディレクトリのプロジェクト自体が利用するreactはどこ
    • /example/node_modules/react

ようは参照しているreactのインスタンスが異なるとダメっぽい。

解決方法

github.com

このIssueに解決策が書かれていて、子のpackage.jsondependenciesにあるReactを親のnode_modulesに指定してやればよい

  "dependencies": {
    .
    .
    .
    "react": "link:../node_modules/react",
    "react-dom": "link:../node_modules/react-dom"
  }

これでyarn -fあるいはrm -rf node_modules/ && yarnてな具合に依存モジュールを再インストールすればOK

子プロジェクトでのReactは親階層である/node_modules/reactを参照するようになるので、同一インスタンスのものが参照されるようになって解決。

lerna あるいは yarn workspace でも解決できそう

参照するReactのインスタンスが異なるのが原因なので、それを回避できればOKっぽい。

lernayarn workspaceを用いると単一の共通したnode_modulesを利用できるので、解決できるはず。

実際自分も最初は↑の単純に親階層にlinkさせる方法にたどり着けなくてyarn workspaceを試していた。

手元でHooksの動作まではうまく行ったのだけど、自分の環境ではRollup.jsでバンドルしていた箇所がどうも壊れてしまって別問題をはらんでしまったのでやめた。(おそらくnode_modulesが複数箇所に分散されるのでそれをRollup.jsが解決できずでなにかしらコケてしまった)

rollup+yarn workspaceでうまくいったよ〜っていう人がいれば教えて欲しい。


余談あるいはバージョニング問題

余談だけど、本件でハマっているときに出くわした別問題で、Reactのv16.7.0にはHooks機能が削られているという罠があった。

  "dependencies": {
    "react": "^16.7.0-alpha.2",
    "react-dom": "^16.7.0-alpha.2"
  }

最初Hooksを試すためにalpha2をインストールしていて、exactオプションなしでインストールしたからチルダ(^)ありになっていた。

semver的にこの状態でyarn upgradeするとv16.7.0のStableに更新されてしまうけど、なんとv16.7.0ではHooksはごっそり削られていた。

  • v16.7.0-alpha.2
    • Hooksあり
  • v16.7.0
    • Hooksなし

Stableリリースでたしかに『Hooksはまだ』とは言われていたけど、alphaの内容継承していると考えるのがなんとなく自然なはずで、upgradeしてから急にHooksが動かなくなってハマってしまった。

yarn add --exact react@16.7.0-alpha.2 react-dom@16.7.0-alpha.2でexact指定しておく必要があった。

こうするとチルダなしでpackage.jsonに書き込まれるので、依存関係のアップグレードをしてもReactがStableに上がることはない。


いろいろ大変という気持ち。

それはそうと見よう見まねで"link: ~"って書いてるけどこれのリファレンスが見つけられない。"file: ~"のリファレンスなら出てくるけど違いが知りたい。

yamlのanchorで継承(extend)して繰り返し書いている部分を減らすメモ

JSばっかやってて.yml書くことはあまりないのだけど、CircleCIのコンフィグとかはyaml記述なので書いていく必要がある。

それで、CircleCIのコンフィグを書いていると、pathとか、複数のjobでそれぞれ同じようなことばかり書いている部分があってDRYじゃないな〜つらいな〜という気持ちで書いていた。

重い腰を上げてググってたところ、JSONと違ってYAMLは継承ができるっぽいので、これを使うとちょいいい感じにできるっぽい。

anchor

&を使うのをanchorと呼んでいるらしい

foo: &foo
  a: 1
  b: 2
  c: 3

これでfooオブジェクトが参照元になる

JSON変換するとこうなる

{
  "foo": {
    "a": 1, 
    "c": 3, 
    "b": 2
  }
}

extend

<<はextendの意味らしい

anchorを継承したいときは<<: *アンカー名のようにする。

foo: &foo
  a: 1
  b: 2
  c: 3

bar:
  <<: *foo
  c: "changed"
  d: 4

なお後述した重複プロパティは上書きされる。この例ではcプロパティを上書きしている。

JSON変換するとこうなる

{
  "foo": {
    "a": 1, 
    "c": 3, 
    "b": 2
  }, 
  "bar": {
    "a": 1, 
    "c": "changed", 
    "b": 2, 
    "d": 4
  }
}

extend inline

オブジェクトの中の単一のプロパティだけ継承元にしたいときは

<<: &アンカー名とする。

こうするとネストしたプロパティのみにアンカーされる。

baz:
  <<: &awesome
    awesome: "cat"
  great: "god"
  bad: "human"

status:
  <<: *awesome
  work: true

JSON変換するとこうなる

{
  "status": {
    "awesome": "cat", 
    "work": true
  }, 
  "baz": {
    "great": "god", 
    "awesome": "cat", 
    "bad": "human"
  }
}

上記一例をオンライン上でみる

YAML Online parser上で見るなら以下です


以下はめっちゃ雑な例

なんか適当に手元でガチャガチャやった例です

データに意味合いはないけど、どういう挙動をしているのか確認したいなら参考になる(かも)

refs:
  <<: &dir
    dir:  /foo/bar
  <<: &favs
    favs: 
      - web
      - music
      - bath
  task: &task
    1: kaimono
    2: souji
    3: renraku
  friends: &friends
    mika: true
    ryo: true,
    yuna: invite
    mike: false
    
im: summer
you:  &you who
he: *you

robot-talking:
  <<: *dir
  she: *you
  name: awesome-robot
  <<: *favs
  task: *task
  <<: *friends

同じくYAML Online parser上で見るなら以下です

参考情報


ひとつ学びになってよかった。書かないと忘れるので書いた。

VS Codeでたまに見えないスペース文字らしきものが混入して文字化けする問題、一時解決した

たまにVS Codeで文字打ってるとエディタ上では可視できない謎の1文字分のスペースらしきものが混入してしまうバグに悩まされていた。

混入していることをしらずにgit pushしたりして、GitHub上で見てみると文字化けしていたりして完全に怒り心頭で😡となっていた。

f:id:hitonatsu:20181215002028p:plain
?が余計に煽ってくる
 

日本語とか中国語とかの特有問題っぽい

ググってたらQiitaの記事出てきて

qiita.com

ここにバックスペース問題と書かれていた。Electronの問題らしくてv2で直ったらしかったけどさっきIssue見てたら「なんかまた再発したわ」みたいなこと書かれていて厳しい。

それで上記のQiita記事で神エクステンションが紹介されていて、入れたら完全に解決してすごい。

marketplace.visualstudio.com

formatterとして機能するのでonSave時にフォーマットするようにしていれば自動で正規表現で問題の制御文字を見つけて削除してくれる!

onSave時にフォーマットしないようにしていても、コマンドパレットに実行コマンドが生えるので手動でもできて便利だった。


今まで文字化けしてたらチマチマカーソル動かして気合いで手動削除したあとにrebaseしていたけど、ホントに助かった。

大感謝という気持ちなので急いで書いた。

TypeScriptで関数の返り値を型に変換する(ReturnTypeを使う)

あるfunctionが返す値から型を作りたかった。

たとえば

const foo = ()=> {
  return {
    foo: 1,
    fooo: 2,
    foooo: 3
  }
}

という関数があって、返り値のオブジェクトの内容で型を定義したかった。

この返り値を直接活かして型に変換して

type Foo = {
  foo: number,
  fooo: number,
  foooo: number
}

こんな型を作りたい。

ためしに適当にこう書いてみたりする

const foo = ()=> {
  return {
    foo: 1,
    fooo: 2,
    foooo: 3
  }
}

type Foo = typeof foo

↓ やったか!?

type Foo = () => {
    foo: number;
    fooo: number;
    foooo: number;
}

やってない、function型だこれ

const foo = ()=> {
  return {
    foo: 1,
    fooo: 2,
    foooo: 3
  }
}

type Foo = typeof foo() // シンタックスエラー

これもダメ……

ReturnType

じゃあどうするかというとReturnTypeというものがTS2.8からビルトインされている。

ジェネリクスを1つ受け取る型で、返り値を型にしてくれる。

const foo = ()=> {
  return {
    foo: 1,
    fooo: 2,
    foooo: 3
  }
}

type Foo = ReturnType<typeof foo>

↓ やったか!?

type Foo = {
    foo: number;
    fooo: number;
    foooo: number;
}

やった

目的達成できてよかったよかった。

参考情報

いくつか用例があるのでこれはエラーになる、直接ジェネリクスに関数定義を置くとこうなる、みたいなのがわかる


使っていきましょう

ターミナルでBrace(波括弧)を展開する

あいかわらず自分はfish-shell使っていて、よくわかってないのでよく困ったりしている。

きょうはBrace…波括弧{}の展開をしたかったけどfish: Mismatched bracketsといわれて困っていた。

どういうことをやりたいのか

$ echo x{boy, girl, man, lady}
xboy xgirl xman xlady

イメージとしてはこういう感じにブレース内の単語に接頭辞を付与して展開したかった。

結論

上の例はミスっていて、実際はこう。

$ echo x{boy,girl,man,lady}
xboy xgirl xman xlady

なにが違うのか

,(カンマ)の後ろにスペース打ってるか否か………。

つまり

$ echo x{boy, girl, man, lady}
fish: Mismatched brackets

$ echo x{boy,girl,man,lady}
xboy xgirl xman xlady

JSに書くときのクセでカンマ後ろにスペース入れたほうが見やすいので無意識に打っていた…。


fish使ってるからfishのことっぽく書いたけど、スペース含めるとダメなのはZSHでも同じなのでスペースを含めるなというはなし。

ただの凡ミスなので休みましょうね〜という暗示な気がする。

.(js|ts)拡張子のReactファイルだとVS Codeでシンタックスハイライトや補完が効かない問題、あるいは.(jsx|tsx)で書くべきか否か

タイトルが長い。

VS CodeでReact書いてるとき、ファイル拡張子が.jsではシンタックスハイライトや補完が効かない問題がある。.jsx にするとちゃんと補完が効いてくれるようになって助かる。

でもみんながみんなVS Code使ってるわけじゃないし、GitHubみてると.jsで書いている人もいれば.jsxで書いているひともいる。

いったいどっちがいいんだ〜 ってなる。

VS Code.(js|ts)でも補完やシンタックスハイライトが効くようにする方法

stackoverflow.com

ここの回答によると

{
    "files.associations": {
        "*.js": "javascriptreact"
    }
}

VS Codesettings.jsonfiles.associationsの設定でjs拡張子に対してjavascriptreactにすればいいらしい!

こうすると.(js|ts)の拡張子でJSX書いても問題なく補完効いたりシンタックスハイライト効いたりしてくれるはず。

拡張子は結局どっちにすべきか

結論からいうと.jsxあるいは.tsxにしておいたほうがベター、ということに自分の中ではなった。

  • .jsxならJSX記述がされていることがファイルを開かなくても察せられる
    • Reactのプロジェクト見てるとき.jsxならおそらくコンポーネントが記述されているはずと予想がつく
  • .jsのままVS CodeにJSXとして認識させるには上記のように設定の変更が必要
    • デフォルトに寄せておくほうがよさそう
    • VS Codeのみならず他のエディタでも同様の問題は起こりそう

という理由から、JSX書くなら拡張子は.jsx、TSで書くなら.tsxにしておいたほうがよさそう。

餅は餅屋、という単純なはなしかもしれない。


関連エントリ

tech-1natsu.hatenablog.com

これはonSave時に保存してフォーマットして欲しくない拡張子があって言語毎に設定のスコープを切っている場合のみにやらないといけない設定。

そんな人はあんまりいない気がするし、スコープ切らないなら.(jsx|tsx)で運用してもこのonSaveの設定はイジる必要はないはず。