https://www.amazon.co.jp/gp/video/detail/B079VR3R95www.amazon.co.jp
観てる。マトリクスとかエクス・マキナとかみたいな雰囲気。
https://www.amazon.co.jp/gp/video/detail/B079VR3R95www.amazon.co.jp
観てる。マトリクスとかエクス・マキナとかみたいな雰囲気。
Kindleストアを眺めていて、あっこれおれ読まないとダメなやつじゃん、と思って読んだ。面白かった。
時間の話を、借金の話とかぶせて説明されたりして、納得感あった。
このへんの文が個人的には熱かった。
教育が良いことであるのはまちがいない 。しかし貧困者が犠牲を払わずに受けられるものであるかのように扱われているが 、実際には処理能力への高い代償がともなう 。本人が集中できずに教育の努力が無駄になるか 、本人は集中するが処理能力に負荷がかかるか 、いずれかである 。本人が実際に訓練やインセンティブに集中するとき 、何に集中しないことになるのだろう ?新たに課された授業はほんとうに 、彼が本を読んだり子どもと過ごしたりするためにひねり出した貴重な時間をつぶすだけの価値があるのだろうか ?
貧困者が貧困に陥る原因は、お金の教育がされていないからだ -> じゃあお金の教育プログラムを受けさせればいいはず。みたいなことがされてるけど、そこには処理能力という見えてない制約があるんじゃぞ、そこも考慮しないとだめよ、というような話。
この本から得られた個人的な教訓としては、借金よくないね、時間あるときに前もってやるべきことやろうね、余裕はある程度持とうね、というごく当然のことなんだけど、それがいかに難しいことか、ということを本書を読んで改めて知ることができた。
自分もこの本に書かれているような失敗を何度もしているので、興味深く読めた。自分は幸い借金はないけど、時間の使い方についてはこの本の言い方でいうとジャグリング状態だ。スラックがない。
何度も読み返しながら、実生活を改善していきたい。
一気に読んでしまうほど面白くはないけど、読みながら一緒に歳をとれて、漫画の中のエピソードが自分の友だちとの思い出みたいな感覚で残っていくので面白い。いまは結婚アフロ田中の3巻が最新だけど、そこから読み始めてもいい。
ウェブフロントエンドは設定周りがひたすらつらくて、なにかプロジェクトを作ろうとするとそれだけで挫折してしまうことが珍しくない。その辺を簡単にするツールもあるけれど、その中身が何をしているものかわからないと、設定の追加や最適化ができなかったりして結局つらいことになったりもする。無駄に難しいことに向き合うのは誰だって嫌なので、ウェブフロントエンドのビルド周りの設定って何が必要なんだ...?というのを、なるべく難しい話を省いて書いておこうと思った。
まず、簡単なウェブサイトにちょっとしたスクリプトを入れるのであれば何もなくてもいい。テキストエディタでindex.htmlとindex.jsを作って、index.htmlに<script src="index.js" />
を書けばいい。ほんとにちょっとしたものであれば、index.htmlに<script>alert('hello world');</script>
と書けばいいだけかもしれない。
簡単なスクリプトでも、1000行近いindex.js
になってしまっていたら、そのプログラムは読むのが大変。なので、他のプログラミング言語と同様に、複数のファイルに分けて、わかりやすくしたい。index.htmlは以下のようになる。
<script src="a.js"> <script src="b.js">
b.jsで、a.jsの機能を使うという依存関係があるため、a.jsはb.jsより先に読み込まれている必要があり、そのため<script src="a.js">
の記述が先に来る必要がある。
これでも困らなければこれで良い。しかしファイルが多くなってくると、管理はどんどん大変になってくる。 そこで、モジュールとして扱えるようにしたくなる。
b.jsで、このように記述して、a.jsからモジュールをimportできるようにする。
import A from 'a'; A.hello();
上のような感じで、モジュール分けられた〜良かった〜。となるものの、ファイル数が多いと、サーバーからJSファイルをダウンロードするのをモジュールの数だけやることになる。HTTP 1.1だとこれがけっこうな時間になりかねない。
そこで、ファイルを束ねて返すために利用できるのがwebpackなどのモジュールバンドラー。 設定を書いて、コマンドを叩くと、複数のモジュールが一つのファイルに束ねられる。なので、それをサーバーから配信するときも1つのファイルを返すだけになって助かる。
ウェブアプリ開発では、モジュールを束ねる以外にもいろいろやりたいことが出てくる。webpackでは、loaderという、バンドル対象のソースコードを変換していくための設定が書ける。
上で書いたimport
などは、そのままだと古いブラウザ(Internet Explorer 11とか)だと動かない。じゃあどうするのっていうことで、古いブラウザで動くJavaScriptにしたくて、それがES5(ECMAScript 5)という仕様。babelというツールがあり、これを使うとES5にできる。だから古いブラウザでも動く。古いブラウザを使っているお客さんにもアプリケーションを使ってもらえる。
JavaScriptは動的型付けの言語なので、静的型付けの言語でウェブアプリを書きたいとなると、だいたいTypeScriptを使うことになる。これも、設定を書けばできる。
これも設定を書けばできる。
これも同上。
...と、ほかもいろいろ羅列しようとすると疲れそうなのでもうやめる。
上に書いてきたものは情報としてあまり価値がないかもしれない。 時間が進んで、古いブラウザを切れるようになってくると前提が変わってくる。すると、必要な設定も変わってくる。コードも変わってくる。 だから、ウェブアプリ書いてる人間はいつまで設定と格闘しなきゃいけないんだつらいっていう気持ちにもなるけど、前提が変わる以上はずっとやらないといけないことだとは思う。
単純に考えると、どういう環境で動くものを作りたいか、どうコードを書きたいかという2点を考えて今後もやっていけばよさそう。
SPAについてのブログ記事を読んだ。自分が考えていることを箇条書きにしておく。
そんな末端を最適化しなくとも、ちゃんと使えるシステムを作っていれば十分なのかもしれないし、逆にそうやって言ってる間にみんな末端を最適化したサービスだらけになっていて競争で勝てなくなるかもしれない。仕事難しい。
ついにきた pic.twitter.com/EjLz6dDspP
— gaaamii (@gaaamii) 2019年2月28日
1年半くらい前に注文したものの、Elmのバージョンが0.18->0.19に上がる都合とかで出版予定日が延びていたElm本がついに昨日、自宅に届いていた。感動した。
勉強になったところとか、感想を書いていく。
Elmといえば静的型付け言語であり、No Runtime Errorなウェブアプリを書けるという良さがある。 一方で、型が読めないと何が何だかわからない。型の話がわからないと、自分で型を書けないのはもちろん、なんといってもドキュメントが読めなくて困る。
この本では、50〜71ページのおよそ20ページでこのElmの型についての説明が書かれている。この辺の節を読むと、制約つきの型変数、type aliasがコンストラクタを生成すること、カスタム型とパターンマッチなどについて学べる。とても丁寧でわかりやすい。特にtype aliasとかカスタム型は自分で勉強したときよくわからなくてつらかった思い出があるので、もっと早くこの本が欲しかった。
Elmにはnullとかnilってものはないという話で、その代わりMaybeって型がある。Maybeはモジュールにもなっていて、withDefault
とかmap
とかandThen
などの関数が生えているのでだいたいやりたいことはできる。なんとなく自分の理解が怪しかったので復習がてら読んだ。
Resultも似たようなものだけど、Maybe以上に自分の理解が怪しかったのはおそらく、APIへリクエスト投げてレスポンス取得して云々みたいな処理を実装しようというときに初めてこのResultに出会ったからだと思う。ElmでHTTPリクエストを送る処理はHttpというモジュールでやる。そこでResultという型が出てくるのだけど、もちろんResult自体はそれ以外のところでも使えるし、Httpのためだけのものでもない。
本書では、
Resultは失敗するかもしれない結果を表すデータ構造です。
と簡潔に書かれていて、例示されているコードもHttpとかは関係のないものになっている。「ElmでAPIへリクエスト投げてデータ取得する実装をやろう」となって、「実装してみたもののResultってやつがなんだかよくわからない」となったときに本書のこの辺を読むとすっきりわかってよさそう。
Html.KeyedとHtml.lazyの話。
ReactでもkeyアトリビュートとReact.PureComponentがあり、これらと同じようなものだと思う。
onUrlRequest
とonUrlChange
の説明がありがたい。
ナビゲーション周りの知識はSPAつくるときに必須なので、これが簡潔にまとめられているのは助かる。
Elmを使って、実行時エラーが発生しないアプリを作れるのは魅力的だけど、新たに言語覚えるのはしんどい。 日本語の書籍にまとまったことで、言語覚えるしんどさが軽減されて、Elm人気もじわじわ高まっていくんじゃないかと思った。
最近、Structuring Reducersとかそこらへんのページを読んでる。
なんで読んでるのかというと、Reduxでウェブアプリケーション書いていて、reducerどう分けるのがきれいなんだっていうのがいまいちわかっていない気がしたから。Immutable.js入れときゃいいのか?とか考える前に、ここを読むべきっぽいので読もうみたいな気持ち。
その中で、combineReducersっていう関数が何をしているのか気になったので、調べた。
combineReducersは複数のreducerたちを、createStoreに渡せる一つの関数の形にしてくれるやつらしい。 実際何をしているのか見てみる。
てきとうにreduxをnode_modulesにインストールしてあるプロジェクトで、nodeのREPLを立ち上げる。
$ node >
そんで、redux.combineReducersという関数を雑に確認
> const redux = require('redux') undefined > redux.combineReducers [Function: combineReducers] > redux.combineReducers.toString() 'function combineReducers(reducers) {\n var reducerKeys = Object.keys(reducers);\n var finalReducers = {};\n for (var i = 0; i < reducerKeys.length; i++) {\n var key = reducerKeys[i];\n\n if (process.env.NODE_ENV !== \'production\') {\n if (typeof reducers[key] === \'undefined\') {\n warning(\'No reducer provided for key "\' + key + \'"\');\n }\n }\n\n if (typeof reducers[key] === \'function\') {\n finalReducers[key] = reducers[key];\n }\n }\n var finalReducerKeys = Object.keys(finalReducers);\n\n var unexpectedKeyCache = void 0;\n if (process.env.NODE_ENV !== \'production\') {\n unexpectedKeyCache = {};\n }\n\n var shapeAssertionError = void 0;\n try {\n assertReducerShape(finalReducers);\n } catch (e) {\n shapeAssertionError = e;\n }\n\n return function combination() {\n var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n var action = arguments[1];\n\n if (shapeAssertionError) {\n throw shapeAssertionError;\n }\n\n if (process.env.NODE_ENV !== \'production\') {\n var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);\n if (warningMessage) {\n warning(warningMessage);\n }\n }\n\n var hasChanged = false;\n var nextState = {};\n for (var _i = 0; _i < finalReducerKeys.length; _i++) {\n var _key = finalReducerKeys[_i];\n var reducer = finalReducers[_key];\n var previousStateForKey = state[_key];\n var nextStateForKey = reducer(previousStateForKey, action);\n if (typeof nextStateForKey === \'undefined\') {\n var errorMessage = getUndefinedStateErrorMessage(_key, action);\n throw new Error(errorMessage);\n }\n nextState[_key] = nextStateForKey;\n hasChanged = hasChanged || nextStateForKey !== previousStateForKey;\n }\n return hasChanged ? nextState : state;\n };\n}'
てきとうなreducerを渡して何がかえってくるかを見てみる
> redux.combineReducers({ hoge: (state, action) => { return state }}) [Function: combination]
関数が返ってきたので、これのなかみを雑に見る。
> redux.combineReducers({ hoge: (state, action) => { return state }}).toString() 'function combination() {\n var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n var action = arguments[1];\n\n if (shapeAssertionError) {\n throw shapeAssertionError;\n }\n\n if (process.env.NODE_ENV !== \'production\') {\n var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);\n if (warningMessage) {\n warning(warningMessage);\n }\n }\n\n var hasChanged = false;\n var nextState = {};\n for (var _i = 0; _i < finalReducerKeys.length; _i++) {\n var _key = finalReducerKeys[_i];\n var reducer = finalReducers[_key];\n var previousStateForKey = state[_key];\n var nextStateForKey = reducer(previousStateForKey, action);\n if (typeof nextStateForKey === \'undefined\') {\n var errorMessage = getUndefinedStateErrorMessage(_key, action);\n throw new Error(errorMessage);\n }\n nextState[_key] = nextStateForKey;\n hasChanged = hasChanged || nextStateForKey !== previousStateForKey;\n }\n return hasChanged ? nextState : state;\n }'
中身を見ると、なるほどたしかにstateのキーごとにreducer(state, action)を呼び出して、次の状態を作っている。
function combination()
というのを見て、なんだ(state,action) => state
の形になってないじゃないかと一瞬思ったけど、よく見るとarguments[0]をstateに、arguments[1]をactionに代入しているので、combination()
は combination(state, action)
と呼び出せることがわかる。関数定義で引数がなさそうでも、引数を渡して呼べて、それをargumentsでアクセスできるらしい1。
呼び出してみる。
> const combination = redux.combineReducers({ hoge: (state, action) => { return state }}) undefined > combination(1, {}) Error: Reducer "hoge" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.
エラーになった。さすがに雑すぎたようだ。このエラー文で言われている通り、初期状態をundefinedにしたらいけないので、そこらへんもちゃんとしたreducerを渡して再度試す。
> const myCombination = redux.combineReducers({ hoge: (state = 0, action) => { return state }}) undefined > myCombination({ hoge: 1}, {type: 'HOGE_ACTION', payload: 'hogehoge'}) { hoge: 1 } >
できた。雰囲気としてactionぽいものを渡しているけど、reducerで利用していないので、空のオブジェクトでも呼び出せる。
> myCombination({ hoge: 1}, {}) { hoge: 1 }
以上で、combineReducersがほんとにただ複数のreducerをまとめるだけの普通のヘルパー関数であることがわかった。よかった。
Reactでは継承よりも、HOCを使うのが推奨されている。HOCとはHigher Order Componentの略で、コンポーネントを引数にとってコンポーネントを返す関数のこと。
なので、たとえば似たような前処理をするページコンポーネントが複数あったとして、その共通処理を書く場合、
export default class UsersDetailPage extends UsersPage { ... }
みたいなことはせず
export default enhanceUsersPage(UsersDetailPage)
みたいな感じになる。
なんで継承じゃなくて関数...?という疑問は当然出てくるけど、以下に
Note that a HOC doesn’t modify the input component, nor does it use inheritance to copy its behavior. Rather, a HOC composes the original component by wrapping it in a container component. A HOC is a pure function with zero side-effects.
と書かれて強調されているように、HOCはもとのコンポーネントの振る舞いは一切変えない。
HOCにコンポーネントを渡せば振る舞いが追加されたコンポーネントとして使えるし、HOCに渡さずそのままコンポーネントを使うこともできる。
SPAを作っていると、何かイベントが起きたときにURL変えつつなにか処理をしたいということがあります。
たとえば絞り込み検索で、クエリストリングだけ書き換えて画面更新したいというケース。具体的には/blog_posts
から /blog_posts?tag=1
へ遷移するような感じです。history.listenにコールバックを渡すと、これが実現できたのでメモしておきます。
interface Props extends RouteComponentProps { hoge: string; } class MyPage extends React.Component<Props, State> { constructor(props: Props) { super(props) } componentDidMount() { this.unregisterHistoryCallback = this.props.history.listen((location: LocationDescriptor) => { this.updateMyPageData(location.search.toString()) }) } componentWillUnmount { this.unregisterHistoryCallback() } private updateMyPageData(query: string) { // ... } private pushHistory(obj: any) { const query = QueryHelper.toQueryString(obj) this.props.history.push({ search: query }) } }
URLつくる -> URLに紐付いた状態更新メソッド的なもの(updateMyPageData
)を呼び出している。
ポイントは、URLが変わったときに呼ぶコールバックの登録と解除。それを history.listen
というメソッドで行っている。listen
は、コールバックを登録してそれを解除する関数を返すので、登録時の返り値を保持しておいて、コンポーネントのunmountのタイミングでそれを呼べば良い。
上記のやり方では props.match.params
(たとえばURLが /users/{userId}/posts?hoge=fuga
のuserId
の部分)を、最新のものを参照できなくて悩んでたのですが、以下の記事を読んで、React Routerのlocationを使えばいいことを知りました。
https://codepen.io/gaaamii/pen/VgrwMvcodepen.io
overflow: visible scroll;
と書いたらx方向ははみ出て見えて、y方向はスクロールになってほしいのだけどそうはいかなくてつらいという話。なんでx方向もscrollになるんだ。
短くてパッと読めて面白かった。主人公が随分変わった人だったけど、共感できるところもあった。生きるの難しいけど、なにか一つでも没頭できる行為なりなんなりがあるのはいいことですな、という気持ちになった。
と、煽ってるような記事タイトルだけど、 煽りたいわけではなく、単にわからないというつぶやきです。
自分はいまCSS Modulesでやっていて、特に困ることがない。
最近styled-componentsのほうがよく聞くのはなんでだろう。 あっちのほうがなにかいけてるポイントがあるんだろうか。