あいつの日誌β

働きながら旅しています。

webpack.config.js で複数のエントリーポイントを指定する

mkdir practice-webpack && cd $_
mkdir -p src/pages www/pages
touch webpack.config.js
npm init -y
npm install --save-dev webpack

create webpack.config.js

const path = require('path')
const webpack = require('webpack')

module.exports = { 
  devtool: 'inline-source-map',
  entry: {
    "app": "./src/app.js",
    "pages/posts": "./src/pages/posts.js"
  },  
  output: {
    publicPath: "/",
    path: path.resolve('www'),
    filename: "[name].bundle.js",
  },  
}

create src/app.js and src/pages/posts.js

echo 'console.log("app")' > src/app.js
echo 'console.log("posts")' > src/pages/posts.js 

webpack を実行する

$(npm bin)/webpack

動作確認

node www/app.bundle.js
node www/pages/posts.bundle.js

Webpack 2: Module not found: Error: Cannot resolve 'file' or 'directory' node_modules/@kadira/storybook/dist/server/addons.js

あらすじ

ある日 webpack の version を上げたら extensions に empty な '' を指定するのだめと怒られるのでその指定を削除したら storybook がなんかエラーを吐き出すようになった時のメモです。

状況

React + Redux で既存の Single Page Application で作っているプロジェクトに storybook を導入しています。 .storybook/webpack.config.js にエイリアアス設置し、 ../webpack.config.js を読み込んんでいます。

原因

現行の webpack は version 2 なのですが storybook が使っているのはどうやら違うらしい。 node_modules/webpacknode_modules/webpack-core名前空間が異なっているから混在できているのが原因?

とりあえず同一の webpack.config.js を version の違う webpack で見ているのが原因らしいので解消したいです。

解消した方法

.storybook/webpack.config.jsエイリアスではなく以下のように実ファイルを保存しました。

const config = require("../webpack.config.js")

config.resolve.extensions.push('') // empty extention を追加する

module.exports = config

おしまい

redux 使わずに react と react-router で SPA したい

あらすじ

redux-form といった ecosystem が要らない場合はこういうやり方もあるんではないかと思います。

やり方

create-react-app practice-react-router-spa && cd $_
touch src/Posts.js src/api.js
npm install --save react-router

edit src/App.js

import React from 'react'
import { Router, Route, IndexRoute, browserHistory } from 'react-router'

const NotFound = () => (<div><span>NOT FOUND</span></div>)

import Posts from './Posts'

export default () => (
  <Router history={browserHistory}>
    <Route path="/">
      <IndexRoute component={Posts.List} />
      <Route path="create" component={Posts.Create} />
    </Route>
    <Route path="*" component={NotFound}/>
  </Router>
)

create src/Posts.js

import React, {Component} from 'react'
import { Link, browserHistory } from 'react-router'
import api from './api'

class List extends Component {

  constructor(props) {
    super(props)
    this.state = { posts: [] }
  }

  componentWillMount() {
    api.getPosts().then((result) => {
      this.setState({posts: result.posts})
    })
  }

  render() {

    return (
      <div>
        <h2>Posts</h2>
        <div><Link to="/create">create</Link></div>
        {this.state.posts.map((p, index) => (
          <div key={index}>
            <h3>{p.title}</h3>
            <p>{p.body}</p>
          </div>
        ))}
      </div>
    )
  }
}

class Create extends Component {

  handleSubmit(e) {
    e.preventDefault()
    const params = {
      title : e.target.title.value,
      body: e.target.body.value
    }
    api.createPost({params}).then(() => {
      browserHistory.push("/")
    })
  }

  render() {
    return (
      <div>
        <h2>Create Post</h2>
        <form onSubmit={this.handleSubmit.bind(this)}>
          <div>
            <label htmlFor="Title">Title</label>
            <input id="Title" name="title" type="text" />
          </div>

          <div>
            <label htmlFor="Body">Body</label>
            <textarea id="Body" name="body" rows="10"/>
          </div>
          <button type="submit">submit</button>
        </form>
      </div>
    )
  }
}

export default { List, Create }

create src/api.js

const posts = [{
  "title": "laboriosam dolor voluptates",
  "body": "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores"
  }
]

function getPosts() {
  return new Promise((resolve) => {
    return resolve({posts: posts})
  })
}

function createPost({params}) {
  return new Promise((resolve) => {
    posts.push(params)
    return resolve()
  })

  // return axios.post(`${BASE_URL}/api/posts`, params).then((res) => {
  //   return { "post": res.data }
  // })
}

export default { getPosts, createPost }

実行

npm start

社内のランディングページなどで試験的に React.js を使って社内用の component を蓄積しつつ React.js に慣れてからの redux を導入するとか、とりあえず React.js でシンプルなページを作るところから始めるのもいいと思います。

おしまい

突然ですがクイズです。 ES6 の module exports の機能として正しいものを選びなさい。

HTML コーダーさんに React components を storybook に追加してもらう作業をしているのですが module exports がわかりづらいので問題をつくってみました。

問題1

関数毎に export されている場合

export const One = () => (
  <div>one</div>
)

export const Two = () => (
  <div>two</div>
)

以下の記述で呼び出しができる。マルかバツか?

import { One, Two } from '../components/Export'

問題2

export default でオブジェクトに包んで返している

const Three = () => (
  <div>Three</div>
)

const Four = () => (
  <div>Four</div>
)

export default { Three, Four }

以下の記述で呼び出しができる。マルかバツか?

import Default from '../components/ExportDefault'

Default.Three
Default.Four

問題3

export default で関数を返している

const Five = () => (
  <div>Five</div>
)

export default Five

以下の記述で呼び出しができる。マルかバツか?

import Default2 from '../components/ExportDefault2'

Default.Five

答え

以下で調べてみてください

create-react-app practice-es2015-exports && cd $_
getstorybook
mkdir src/components

Export.js

cat << EOS > src/components/Export.js
import React from 'react'
export const One = () => (
  <div>one</div>
)

export const Two = () => (
  <div>two</div>
)
EOS

ExportDefault.js

cat << EOS > src/components/ExportDefault.js
import React from 'react'
const Three = () => (
  <div>Three</div>
)

const Four = () => (
  <div>Four</div>
)

export default { Three, Four }
EOS

ExportDefault2.js

cat << EOS > src/components/ExportDefault2.js
import React from 'react'
const Five = () => (
  <div>Five</div>
)

export default Five
EOS

stories

cat << EOS > src/stories/index.js
import React from 'react';
import { storiesOf, action, linkTo } from '@kadira/storybook';
import { One, Two } from '../components/Export'
import Default from '../components/ExportDefault'
import Default2 from '../components/ExportDefault2'

storiesOf('Answer', module)
  .add('=>', () => (
    <div>
      <One />
      <Two />
      <Default.Three />
      <Default.Four />
      <Default2 />
    </div>
  )); 
EOS

Hachioji.pm #63 に行ってきました

https://atnd.org/events/85292

hachioji.pm #63 に参加してきました。#0から始まっているので64回目です。 Hachioji.pm #37 以来なのでちょうど3年ぶりらしい。そんなに時間経ってるんだ...

八王子の居酒屋で未来を体験する

f:id:okamuuu:20170128190351j:plain

LT一覧

酔っ払いながらメモしているので情報が正確でないかもしれませんのでご了承ください。

hkoba: git submodule のお話

独自のディレクトリ構成のサービスに抵抗するために開発したシェルスクリプトを紹介してくれました。

hirobanex: Line bot API のお話

なぜか資料がマインドマップ。ピャッとしたりヒャッとしたり。

hide_o_55: Rust のお話

今年は Rust をやるそうです。次は Rust が来るのか!?

myfinder: Azure なら Perl で Line Bot が書けるお話

Live Coding で Azure Functions を使った Line Bot 実装を紹介してくれました。

からし: メンテナンスの重要性のお話

メンテナンスって大事ですよね。靴磨きに例えたのは斬新。経営者の方々にはぜひ地球と同じぐらいエンジニアの命を大切にしてほしいです。

pandax381: 初参加だけど実は八王子在住のエンジニアのお話

仕事内容が tcpdump な ネットワークとインフラのエンジニアさん。普段私が関わらないレイヤーの話が隣で聞けるのは貴重な体験でした。難しくて内容がよくわからない自分が残念。

私:フロントエンドエンジニアのお話

実はフロントエンドエンジニアってすごくないですか?もうちょっとフォーカスしたほうがいいんじゃないでしょうか。というお話をしました。 react-storybook をもうちょっと紹介しておけばよかったかも。

https://okamuuu.github.io/talks2/storybook-static/

uzulla: エディタのお話

typora でシーケンス図とか作れて便利だったり炎上の話とか

makamaka_at_donzoko: 納期が襲ってこないお話

現実世界でそうあってほしいですね。

f:id:okamuuu:20170128222148j:plain

それから雑談

いろいろな立場の人がいらっしゃるので日頃感じていた疑問を投げかけていました。

雑談1: プログラマ初学者におすすめの言語

注: 酔っ払いが酔っ払い達と会話して酔っ払った時に記録したメモなのでところどころおかしいと感じるところがあると思いますがご了承下さい。

プログラミング初学者におすすめの言語はなんでしょうという質問をしたら色々な人からいろいろな見解が得られたので楽しかったです。「初学者には言語の選択よりもチューターの有無のほうが重要」という意見に概ね全体が同意してました。ということでチューターがいる人はその人が得意な言語でいいんだと思います。

他にも PHP は最初に composer や bundler などの使い方を覚えなくても WEB サービスを作るのに必要なものが入っているのでそういった点ではおすすめという意見をもらいました。開発環境の構築は初学者がつまずくとやる気でないから結構大事なことだと思います。

それから JavaScript だったらフロントエンドもサーバーサイドも使われるので汎用性が高いのでは?という意見もありました。 なんですが、私個人の意見だとちょっと技術の革変が急すぎるのと非同期処理が初心者に大丈夫なんだろうかという心配はあります。 非同期な処理を逐次でループするプログラムを JavaScript で書くのがちょっと面倒..

ということでチューターがいればその人のおすすめする言語がいいと思います。 チューターがいない人は PHP 案外いいのかな、と個人的には思いました。

そういえば hachiojipm の主催者はとても PHP に精通しているのでお困りの方は是非 hachiojipm にいらしてはいかがでしょうか。

雑談2: LL 系の言語の求人なのに、他の LL 系の言語の経歴を考慮しないのはなぜ

注: 酔っ払いが酔っ払い達と会話して酔っ払った時に記録したメモなのでところどころおかしいと感じるところがあると思いますがご了承下さい。

「別にその言語が未経験だから採用しないという事はないと思う。縁故採用というものがあるし、経験がなくても対応できるという実力が証明できれば問題がないのでは?」という意見があり、とても腑に落ちる話でした。 そもそも経歴書だけで「経験がなくても対応できるという実力」があります、という主張が通らないほうが普通なんでしょうね。 そういえば一度 Python の仕事をした時があったのですが面談に行ったら hachiojipm で会いましたよね。みたいな事があったのであれも一種の縁故採用なのかな?

というわけでそんな縁が生まれる事もあったりするので皆さまも是非 hachiojipm にいらしてはいかがでしょうか。

まとめ

職場にもエンジニアが多数いらっしゃるのですが hachiojipm にはより個性の強い人たちが集まるので色々勉強になると思います。 みなさまも是非 hachiojipm にいらしてはいかがでしょうか。

ちなみに

新宿から八王子までは JR よりも京王線のほうが安いです。

React tutorial (9)

Datetime Picker も使用頻度が高い、というよりもほとんどマストなので記事をひとつ追加します。 Bootstrap 風のデザインの react-datetime を最後に紹介します。

github.com

この他にも airbnb/react-dates など沢山モジュールがありますので、各自でいろいろ試すと良いと思います。

依存モジュールを追加

npm install --save moment react-datetime

修正箇所

git diff src/components/Form.js

 import React, { Component } from 'react'
 import { Field, reduxForm } from 'redux-form'
+
 import Select from 'react-select'
 import 'react-select/dist/react-select.css'
 
+import "react-datetime/css/react-datetime.css"
+import DateTime from 'react-datetime'
+
 export const renderField = ({ input, label, type, meta: { touched, error } }) => (
   <div className="form-group">
     <label htmlFor={label}>{label}</label>
@@ -52,3 +56,14 @@ export const renderSelectMultiField = ({ input, label, options, meta: { touched,
     </div>
   )
 }
+
+export const renderDatePickerField = ({ input, label, meta: { touched, error } }) => (
+  <div>
+    <div className="form_label">{label}</div>
+    <div className="textarea_icon">
+      <DateTime value={input.value} onChange={param => input.onChange(param)} dateFormat={"YYYY/MM/DD"} timeFormat={false} />
+    </div>
+    <p className="text-danger">{touched && (error && <span>{error}</span>)}</p>
+  </div>
+)
+

git diff src/containers/CreatePost.js

 import { connect } from 'react-redux'
 import { Field, reduxForm } from 'redux-form'
 import { createPost } from '../actions'
-import { renderField, renderTextAreaField, renderSelectField, renderSelectMultiField } from '../components/Form'
+import { renderField, renderTextAreaField, renderSelectField, renderSelectMultiField, renderDatePickerField } from '../components/Form'

まとめ

という事で、この Tutorial を一通りやってみると、もしかすると SPA を React.js で書けるようになる気がします。なるといいですね。