旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

レイアウトのためのatomコンポーネントとかtemplates層とかの実装

Atomic Designの本「Atomic Design ~堅牢で使いやすいUIを効率良く設計する」を参考にしながら、こういう感じでやっていってる。

// atoms/AppLayout.tsx
const AppLayout = (props: Props) => {
  const children = React.Children.toArray(props.children)

  return (
    <div className={styles.left}>{children[0]}</div>
    <div className={styles.right}>{children[1]}</div>
    <main>{children[2]}</main>
  )
}

// templates/UserPageTemplate.tsx
interface Props {
  user: UserInterface;
}

const UserPageTemplate = (props: Props) => {
  return (
    <AppLayout>
      <AppHeader />
      <AppSidebar />
      <UserView user={props.user} />
    </AppLayout>
  )
}

AppLayout に当てるCSSのflexboxなりgrid layoutなりでレイアウトを実現しておいて、同じレイアウトのページを作成するときは同じようにAppLayoutで囲えばそのレイアウトになるという感じになる。

templateのコンポーネントでは、どのレイアウトにどのOrganismを置くかということを記述する。URLから最初の状態を作ってpropsとして渡すのはその上のpageコンポーネントにやってもらう。なのでtemplateはStateless Functional Component (状態無し関数コンポーネント)になる。

レイアウトがAtomだってことに最初はちょっと違和感あったけど、別に何もおかしなことはなかった。

26歳

忘れてた。26歳になっていました。若者を自称するのはもはや不可能になり、身体能力も着実に衰えてきています。この前テニスしたら大学生にロブで振られてボロボロにされ、負けました。翌々日の筋肉痛が全てを物語っていました。つらい。26歳も頑張ります。

キウイの本を読んでる

キウイ🥝。

なんで読んでるのか

自分はいまウェブアプリケーションのUIをつくる仕事をしているので、ReactやらReduxの記事とかはよく読む。けどもう少し広めの、UIをつくるプログラミング全般に適用できる設計パターンみたいなのを学びたかった。

あとで感想ここに書く

Storybookを使って気付くCSSの粗さ

がっと作った大きなOrganismがあって、それを構成するMolelculeやらAtomやらのレベルのコンポーネントをすべてStorybookに置いた段階で、「あれ、なんかこれいい感じに表示できない...」ということに気付く。CSS Modulesを使ってスタイルのスコープをそのコンポーネントに閉じさせているものの、それだけでは期待する見た目にならない。正しい見た目が上の要素に依存していて、それがないとちゃんとした見た目にならない。直さなくては...ということになる。

Storybookに細かい単位でコンポーネントを置かなければこういうのをごまかせてしまうので、いざ細かいコンポーネントを使い回すときになるまで「なんだこれ」ということに気づかなかったりする。Storybookの使いみちはいろいろあるけど、こういうスタイルのスコープ確認につかえて便利だと思う。

fetch-mockでStorybookからモックデータを利用する

新たに開発している画面で、Storybookを利用してデザイナーさんに見た目のレビューしてもらいやすくしようとしている。この画面はAPIと並行して開発しているので、モックデータを用意してそれを見るようにする必要がある。

アプリケーション本体の方ではjson-serverを使っていて、Storybookもローカルで確認するにはそれで十分だったのだけど、確認のために用意した環境はS3なので、別でモックサーバーを立てるというのはそこまでするの感がある。

ferch-mockを使うと、APIへのリクエスト部分をモックのpromiseに差し替えられる。setTimeoutの値を変えれば遅いAPIをシミュレーションできるし、エラーを返せばエラー時の挙動を確認できる。便利。

json-serverのために用意したモックデータをそのまま使えてよかった。

ContainerコンポーネントとPresentationalコンポーネント、reduxとの接続についての方針とか

ReactとReduxでアプリケーション書いていて、containerコンポーネントとpresentationalコンポーネントの分け方どうするかという話。スマホで雑に書いているので、あとでまとめる。

元ネタ

redux作者のブログ記事のあれ。

参考にしたもの

AbemaTVの人のAtomic Designの本に書いてあった方法

コンポーネントの種類分けることで何ができるのか

もう少し詳しく

  • イベントが起きたときに何をするかというのをcontainer側に書いておいて、見た目のコンポーネントにはそれをpropsとして渡す
  • reduxのstateに変更を加えるような処理はactionを投げてreducerが処理をする
  • 見た目コンポーネントがそのコンポーネント自身でしか使わないような見た目のStateを持つのは許容する。
  • ファイルは今のところ分けずに、1ファイル内にcontainerもpresentationalも書いてる
  • reduxと接続するのはcontainer

elm/timeの使い方よくわからない問題

あるイベントが起きた時刻をサーバーに送信したい!というときに、Elmではどう書くのか調べたけどよくわからなくて、頑張って実現するとこういう感じになってしまったというメモ。これでいいのかよくわからない。サンプルを書いたので識者の知見がほしい...。

サンプル

疑問点

JSなら任意の時点で new Date()すれば現在時刻が取れるのだけど、Elmの場合はsubscriptionsに関数を入れて、一定間隔でmodelの値を更新して、使うときはその値を参照するみたいにしないといけないのか...?というところ。イベントが起きたときだけほしいのに、前もってそれをmodelに持って常に更新しておかないといけないのだとしたらなんか変な感じする。おれがやってることが間違ってるんじゃないか感がある。というのが現状。

Reduxのreducerはなんでreducerと呼ばれているのか

Reduxのreducerはなんでreducerなのか、そもそもreducerってなんだ、みたいなことを調べたので書きます。間違ったこと書いてたら直します。

背景

Reduxでは、reducerってやつが状態の変更方法を知っています。reducerはReduxのドキュメントに以下のように書いてある通り、

状態とアクションを受け取って、次の状態を返すやつです。

しかし、reducerという言葉はどういう意味なのでしょう。「状態とアクションを受け取って、次の状態を返すやつ」を、なぜreducerと呼ぶのでしょう。

Weblioの辞書で調べると、

〔+目的語+to+(代)名詞〕〈ものを〉(整理して)〔簡単な形に〕変える,まとめる.

という意味が出てきます。上述の通りReduxのreducerはstateを変えたものを返すやつなので、これが適切なのではないでしょうか。

ここで納得して終わりにしてもいいのですが、Reduxのreducerの意味がこれでよしとするには少し早いような気がします。Reduxの文脈で、なぜreducerという言葉が使われているのかをもう少し調べてみます。

Why is it called a reducer?

Google検索で、雑に why reducerと検索すると、先程のページが出てきます。

疑問に対する答えに当たる部分はここでしょうか。

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

ふむ。Array.prototype.reduce(reducer, ?initialValue)として渡せるような関数だからreducerと呼ばれている…?

なるほどわからん

Array.prototype.reduceの仕様

なるほどわからんなのは、私がArray.prototype.reduce関数を普段使っていないからのような気がします。reduceという関数に慣れていれば、上記の説明で一発理解ということになるのかもしれません。reduceは使ったこともあるし他の言語でもだいたいあるような一般的なものですが、ちゃんと仕様を読んで理解しているわけではありませんでした。

そこで、このreduce関数の仕様を見てみます。

さすがMDN、説明が丁寧です。これもじっくり読んでいきます。

reduce() はアキュムレータと配列の各要素に対して(左から右へ)関数を適用し、単一の値にします。

なるほどわからん。今度はまた新たななるほどわからんです。

今度のなるほどわからんは、「アキュムレータ」ってなんだ、という話です。こういうことを正直に書いてしまうとコンピュータのこと知らんやつだなというのが露呈するのであれなんですが、わからないものは仕方ない。

アキュムレータ(英: Accumulator)は、コンピュータにおいて、演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のことである。

accumulateというのが累積するという意味の英単語なので、そのままAccumulatorは累積するやつのようです。コンピュータアーキテクチャのことを学びたい気持ちも湧いてきてしまいますが、MDNのreduceのドキュメントを理解する上では、accumulatorという言葉の理解は上の説明で十分でしょう。

もう一度MDNのドキュメントに戻って、reduceが何をしているのかを見ていきます。

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

Reduceの使い方として、上のコードが例示されています。配列内の数の総和を出しているだけです。

やっていること、使い方はわかりますが、どう動作しているのでしょう。それについても書かれています。

なるほどわかりました。accumulatorがそれまでの返り値を保持していて、currentValueが配列内の現在の要素です。

let array = [1,2,3,4];
let accumulator = 0;
for (let i = 0; i < array.length(); i++) {
  let currentValue = array[i];
  accumulator += currentValue;
}

for文で書くとこんなかんじでしょうか。外で宣言した変数に詰めるのではなく、引数として渡ってくるのでreduceのほうがすっきり書けます。

Array.prototype.reduce?

Array.prototype.reduceがやっていることはわかったものの、

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

これは結局どういうことでしょうか。ReduxのreducerがArray.prototype.reduce(reducer)として呼べるのであれば、reduceのレシーバに当たる配列は何になるんでしょうか?

Stack Overflowのある回答がとてもわかりやすかったです。

['INCREMENT', 'DECREMENT', 'INCREMENT'].reduce(reducer, 0)

つまりアクションの配列がレシーバになる。なるほど〜〜〜、そういうことを言っていたのか!0が最初のstate(accumulator)になり、reducerで定義された変更が3つ行われた結果が返ってくる。

雑感

Reducerというのが「stateを変えるためのあれ」くらいの認識だったけど、今回の調べで言葉の意味から理解できた。

ところで、reduceを調べているとfoldとか折り畳み関数みたいな言葉も出てきていた。次はここらへんについてもまとめてみたい。

参考リンクなど

RubyWorld Conference 2018 参加記(2日目)

shgam.hatenadiary.jp

1日目に続き、2日目も参加しました。

Chad Fowler氏の基調講演 Don't Stop Moving

エンジニアのキャリアなどについての話でした。そういえばまだちゃんと情熱プログラマー(Chadさんの本)を読んだことなかったので読みたくなりました。

mrubyについて

2日目はmrubyの話が多かったです。おもむろにrbenv install mruby-1.4.1してから話を聞いていましたが正直あまりついていけませんでした...。後でmrubyについて調べつつ発表の動画を見返したい。

2日間通しての感想

運営も会場も内容もすべてしっかりしたカンファレンスでした。

とても貴重な経験ができました。様々なRubyistと話すことができましたし、島根県松江市Rubyの関係とか雰囲気とか、そういったものも感じることができました。プログラミング言語で町おこしというのは先例もなかったと思うのですが、市がRubyの価値を理解してそれを利用してるというのがすごい。

また、自社のスタディプラスブースで好きなRuby本のアンケートを取っていたんですが、これがとても面白かったです。 Rubyのしくみ -Ruby Under a Microscope-が思ったより得票数多くて、さすがRubyWorld Conferenceだ...!という感じでした(青い付箋がRubyのしくみ)。

RubyのしくみはRuby構文解析とかの説明をした本なので、Rubyそのものに興味がある人達が集まっているこのカンファレンスでは人気が出るのもそれはそうか、とも思いましたが、それにしてもすごい。

自分はRubyのしくみをけっこう序盤で挫折してから読んでいないのですが、なんとかまた読み直そうと思いました。

2日間、自分は講演の聴講と自社ブースの案内だけで発表などはしていませんが、それでもとても貴重な経験ができました。 関係者の方々、スポンサーになってくれた自社に感謝です。ありがとうございました。

RubyWorld Conference 2018 参加記(1日目)

会社がスポンサーになったので、こちらに出席させていただいてます。

2018.rubyworld-conf.org

1日目のプログラムはまつもとゆきひろ氏の基調講演から始まり、Ruby Prizeの発表で終わりました。

Matz基調講演(The power of the community)

Rubyの父、Matzことまつもとゆきひろ氏の基調講演。

この基調講演では、Rubyがいつどのように生まれて、今日のように人気のある言語として広く使われるようになったのかを聴くことができました。

放置していたUTF-8対応、ドキュメント整備、ミートアップ開催、本の出版などはどれも、まつもとさん個人の力ではなくコミュニティの力で行われたことであり、特に人にプログラミングを教えるのはまつもとさんが苦手な部分だった(プログラミングできない人の気持ちがわからない)という話がありました。

また、こういったコミュニティの力を借りるために、コミュニティに対して面白いものを提供し続ける必要があるというようなことをおっしゃっていました。OSSコミュニティは止まったら死ぬ鮫のようなものだ、とも。

私はユーザーとして普段からOSSを使って仕事をしている身ですが、未だにOSSコミュニティは不思議に感じる部分が大きいです。仕事とも個人の趣味とも言えない、独特の雰囲気があります。

この基調講演でまつもとさんも、コミュニティに関わる人のモチベーションは知的好奇心、承認欲求、そこで発生するコミュニケーション、責任感、お金など人それぞれだとおっしゃっていました。

Ruby Prizeを受賞したk0kubunさんの発表

Ruby Prizeを受賞したk0kubunさんの発表も凄まじく良かったです。途中までブースにいて、内容を途中から聴いたのでYouTube動画あとで見返したい。

感想とか

今、Rubyはメジャーなプログラミング言語で、仕事はたくさんあり、学ぶ価値があるというのはすぐわかります。私も仕事で扱っているシステムはRubyで書かれていて、その開発や保守に携わって、日々生活するためのお金を頂いてます。Rubyで書かれたシステムが動いて、実際に価値を生み出していることは感じています。

しかし、そういったことが可能になる前からRubyそのものに携わってきた、作者のまつもとさんやコミュニティの中心で関わっている方々には、それとはまた違う気持ちがありそうです。

今日の講演などを聴いた上での私の想像ですが、知的好奇心を満たす手段として、純粋に楽しくてRubyに関わっていたのかなと、そんな風に思います。

何か技術的なことを学んだり、ものを作ったり、プログラムを書くことが、今の私にとっては仕事をしてお金を稼ぐためのものであり、それ自体が目的にはなっていません。しかし、もしそれ自体が目的になったり、少なくともそう感じられることが増えれば、とても幸せなことだと思います。

すごいエンジニアになってたくさんお金を稼ぐとか、偉くなりたいとか有名になりたいとか、そういったことではなく(もちろんそれも大事なんだけど)、やっていること作っているものそのものに没頭して、楽しさや喜びを見いだせるようなエンジニアになろう。基調講演やほかの素晴らしい発表を聴きながら、そんなことを考えることができました。

今さらReduxのSSOT原則とか状態管理について考える

Reduxを使ってウェブアプリケーションの開発をしていて、割と早い段階で、どうしてもReactのsetStateを呼びたい場面に遭遇しました。しかしReduxといえば、Single Source of Truthという原則があります。状態はすべて、Redux側で一つの状態ツリーで管理するものだと思っていました。

とすると、Reactコンポーネントたちは状態をpropsとして受け取って描画するだけ、ただのビューの関数であり、Reduxでの状態管理とReactでの要素表示の役割がしっかり分けられてきれいな世界になる...!!これがいまどきのきれいなウェッブアプリケーションなんじゃ!!!実装を始める前まではそんなものを想像していました。

ところが、実装を始めるとすぐにReactのsetStateを呼びたくなりました。それはもうすぐに呼びたくなりました。何かと言うと、いわゆるドロップダウンメニューの開閉表示状態です。今私が利用しているはてなブログの編集画面の、ここの部分みたいなものです。

クリックしたら開いたり閉じたりします。これ系の挙動をするコンポーネントは、だいたい何を作るにしても必要なものだと思います。

要は、この開閉状態を、Reduxで管理せず、Reactのそのコンポーネントのstateに持たせたくなったのです。

Reduxで管理する場合、きっと以下のようなコードが必要になります。

// components/DropdownComponent.jsx
const mapStateToProps = ...
const mapDispatchToProps = ...
export default connect(mapStateToProps, mapDispatchToProps)(DropdownComponent)

// actions/ToggleDropdown.js
export default {
  type: 'TOGGLE_DROPDOWN'
}
// reducers/toggleDropdown.js
const toggleDropdown = (state, action) => {
  switch(action.type) {
    case 'TOGGLE_DROPDOWN' :
    default:
      return !state
  }
}
export default toggleDropdown 

ふむ...ずいぶんと多くのファイルを編集しないといけない。

対して、ReactのsetStateを使う場合。

export default class DropdownComponent {
  constructor(props) {
    super(props)
    this.state = {
      visible: false
    }
  }

  handleClick = (e) => {
    this.setState({ visible: !this.state.visible })
  }

  render() {
    <div>
      <button onClick={this.handleClick}>
      {this.renderContent()}
    </div>
  }

  renderContent() {
    if (this.state.visible) { 
      return <DropdownContent />
    } else {
      return null
    }
  }
}

こりゃいいや!!!このコンポーネントのなかで完結していてわかりやすい!

しかし、そんなことをして怒られないのでしょうか。この軽はずみな実装によってReduxの怒りを買ってしまいプロダクトが破綻してしまっては困ります。

調べてみると、ちゃんとReduxにこんなFAQがありました。

(質問)

Do I have to put all my state into Redux? Should I ever use React's setState()?

まさしく自分が知りたいことです。setState呼んでいいのでしょうか?

(回答)

There is no “right” answer for this. Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state.

なるほど、、全部Reduxで管理するやつもいるし、ドロップダウンの開閉状態みたいなものに関してはコンポーネントに持たせる人もいるよね、と。答えになってないじゃないか!

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

それはお前が考えることだ、と。ふぉ〜〜〜。そうですよね〜〜。

改めて考えてみると、もともとはReduxなんかなくてもアプリケーションは作れる。じゃあなんでRedux使うのかというと、コンポーネントの中ではなくアプリケーション全体で持ち回したい状態を一箇所でちゃんと管理したいから、ということになる。それだってRedux使わなくてもできるわけだけど、そうするとバケツリレーと呼ばれるあの、状態とコールバックを受け渡し受け渡しする見通しの悪い地獄みたいなコードを書かないといけなくなる。それは嫌だ。それだったらReduxを使う、となります。

まとめ

ReduxのSingle Source of Truthを厳密に守ろうとするのはきついと感じることもあり、しかしReduxみたい状態管理ライブラリは何かしら必要です。自分の精進が足りず、まだベストな方法を見つけられていない感がありますが、Reduxを完璧なフレームワークと思い込まず、問題があったらそれについて考えながら使いこなしていければと思います。