Github二段階認証後はID + tokenを使う
https://github.com/settings/security
困ったこと
会社のルールでGithubで二段階認証(SMS)をおこなっていたのですが、コンソールでID+パスワードを求められた際に認証不足で処理が通らなくなります。
こんな場合です。
$ git clone https://github.com/ttaniguchi/private-repository.git Cloning into 'private-repository'... Username for 'https://github.com': ttaniguchi Password for 'https://ttaniguchi@github.com':
対処方法
パスワードの代わりにPersonal access tokensを作れば大丈夫でした!
https://github.com/settings/tokens
これを知らないと二段階認証への移行に結構足踏みしそう… メモがてら共有します。
React+Redux環境でreact-router@v4への移行をおこなう
React Routerの4系がリリースされて久しいですが、相変わらず対応ができていませんでした。 ようやく重い腰を上げてアップデートしたのですが、噂に違わず障壁が多い移行だったため、メモがてら記録を残しておきます。
v3 → v4で何が変わったか
簡潔に言うと、とても良くなりました。
- ルーティングコンポーネントのネストについての制約(縛り)が解消された。
- contextを利用せずにパラメータを送れるようになった。
現在の状況
- React+Reduxで構築されたサービスです。
- react-router3系とreact-router-reduxを併せて利用していました。
- 画面遷移はhistroyに対するrouter.push()がメインです。
<button onClick={context.router.push()} />
移行作業
パッケージの更新
今回更新したパッケージは以下の通りです。
react-router@3.0.0 → react-router-dom@4.2.2
- DOMのみパッケージ分割されていましたが、こちらで事足りました。
react-router-redux@4.0.8 → @next
- 4系への対応は@nextバージョンを利用するように、とのことでした。
- それ以前のバージョンは2/3系用となっています。
query-string
- クエリパラメータ取得に使います。
ルーティング部分
新たなルーティングがこちらです。
import React from 'react'; import { render } from 'react-dom'; import { HashRouter, Route, Switch } from 'react-router-dom'; import { createHashHistory } from 'history'; import { Provider } from 'react-redux'; import { ConnectedRouter } from 'react-router-redux'; : const history = createHashHistory(); const store = configureStore(history); render( ( <Provider store={store}> <HashRouter> <ConnectedRouter history={history}> <NaviComponent> <Switch> <Route exact path="/" component={Top} /> <Route path="/contents/:id" component={Contents} /> <Route path="/contents" component={Contents} /> </Switch> </NaviComponent> </ConnectedRouter> </HashRouter> </Provider> ), document.getElementById('app'), );
- ConnectedRouterがreduxと連結したhistoryを注入してくれます。
- Hashによるルーティングを利用しているのでHashRouterで括っています。
- NaviComponentのように共通のコンポーネントはそのまま組み込めます。
- Switchで括っておくと、マッチするRouteが複数あっても最初にマッチしたコンポーネントしか表示しません。
- pathは"/contents(/:id)"と書けなくなってしまったため、2行に分けて記載しています。
- もっと良い方法があればコメントなどください。
- URLパラメータの:id引き渡しについても以下の通り変更がありました。
- props.params.id + props.match.params.id
アクションの置き換え
- push / replace / go / goBack等のファンクションはcontext.router内から取得していましたが、withRouter()メソッドの登場によって、Route直下にない孫コンポーネントにもprops経由でこれを送ることができるようになりました。
import { withRouter } from 'react-router-dom'; : export default withRouter(Component);
- また、相対パスが利用できるようになったことも注意が必要でした。
- this.context.router.push('contents'); + this.props.history.push('/contents');
contextを廃止
公式ドキュメントにもあるように、contextは非推奨仕様でした。そもそもReactRouterの実装のために用いていたので、これを機にすべて撤廃することにしました。
クエリパラメータの対処
const search = props.location.search; // ='?key1=hoge&key2=fuga' const params = perse(search); // = { // key1: 'hoge', // key2: 'fuga', // }
- また、Switchからも見て取れるように、パスでのみコンポーネントが切り替わるためクエリの変化はpropsに流れてこなくなりました。
所感
主に詰まったところしか記載していないのですが、大概の情報はこちらのページで仕入れる事ができました。 reacttraining.com
以下のサイトにもお世話になりました。 qiita.com
今回のredux連携のようなケースは単体でのパッケージ導入と異なり、公式ドキュメントだけだとなかなか追いづらいところもありましたが、開発者側も周辺パッケージを意識しているようで、今回のアップデートはかなりreduxに寄せたものだったとも思いました。
今後もナレッジについてはメモがてら残していこうと思います。
補足
1018/05/18訂正
サンプルのコードで、HashRouter と ConnectedRouter のネストが逆になっていました。今のコードは訂正後のコードになっています。
HashRouter から振ってきたものを history 反映しないとダメですよね… 手元で起きた不具合としては、「history が一巡遅れて」いました。
Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門
- 作者: Stoyan Stefanov,牧野聡
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/03/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: 久保田光則
- 出版社/メーカー: 技術評論社
- 発売日: 2017/05/26
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
Reactで絶対パスによるimportを使いたい
コンポーネントが深くなるとパスが読みづらい
ecmascriptでは外部ファイルを取り込む際に相対パスを指定します。
ただ、コンポーネントの階層が深くなるに連れて相対パスがわかりづらくなるので、絶対パスが使えればと思うケースも出てきます。
-import Error from './../containers/ErrorContainer'; -import Loader from './../containers/LoaderContainer'; -import locale from './../libs/Locale'; -import DevTools from './../DevTools'; ↓ ↓ ↓ +import Error from 'jsx/containers/ErrorContainer'; +import Loader from 'jsx/containers/LoaderContainer'; +import locale from 'jsx/libs/Locale'; +import DevTools from 'jsx/DevTools';
babel-plugin-module-resolverを使う
Babelifyの際にパスを解決できれば絶対パスも利用可能です。 それを実現するライブラリがこちら。
利用方法
npmでインストール
npm install -S babel-plugin-module-resolver
/.babelrcファイルに設定
{ "plugins": [ "transform-inline-environment-variables", ["module-resolver", { "alias": { "jsx": "./jsx", // componentを置くディレクトリを絶対パス化 } }] ] }
aliasとしてディレクトリ名を登録できるので、相対パスもそのまま利用できます。 npmパッケージと被らない名称であれば複数aliasを増やせます。
eslintも通したい
Babelで解決させた後はeslintでもパスを許容するよう修正します。 eslint-import-resolver-babel-moduleを利用するのが便利です。
利用方法
npmでインストール
npm install -S eslint-import-resolver-babel-module
/. eslintrcファイルに設定
{ "settings": { "import/resolver": { "babel-module": {} } } }
これで絶対パスもlintが通るようになります。
ただ、VSCodeなどでタグジャンプなどを利用している場合、うまく動かなくなる可能性があります。こちらについては別途プラグインなどを調査中です。(見つかったら加筆します)
react-router画面遷移時のスクロール位置を操作する
前のページのスクロール位置が残る
react-routerはURLのハッシュを用いて画面遷移を表現していますが、実際のルーティングは固定だったりします。
なので、こんな気遣いをしないと遷移後に最上部にスクロールしてくれなかったりします。
<Router onUpdate={() => window.scrollTo(0, 0)}> : </Router>
新しいプロジェクトを作成したときに、どうやってたっけ?と思ってしまったのでメモがてら残しておきます(^_^;)
ちなみに、ルーティングにパラメータを付けて、デフォルトのスクロール位置を変更させたい時はこちらのような共用モジュールを作成して対処しています。
自分の位置を返すComponent
import React, { Component } from 'react'; import PropTypes from 'prop-types'; export default class Scroller extends Component { componentDidMount() { const { active, callback } = this.props; if (active) { callback(this.scroller.offsetTop); } } componentWillUnmount() { this.scroller = null; } render() { return ( <div ref={e => (this.scroller = e)} /> ); } } Scroller.propTypes = { active: PropTypes.bool, callback: PropTypes.func, }; Scroller.defaultProps = { active: false, callback: null, };
Scrollerはスクロールさせたい位置にマーカー的に配置します。
activeの場合のみコールバックで自分の位置を返します。
使い方はこんな感じです。
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Scroller from './Scroller'; export default class Clips extends Component { componentDidUpdate() { if (this.position) { window.scrollTo(0, this.position); this.position = null; } } render() { const { location } = this.props; return ( <div> <Scroller active={location.query.content === 'content1'} callback={e => (this.position = e)} /> <div> コンテンツ1 </div> <Scroller active={location.query.content === 'content2'} callback={e => (this.position = e)} /> <div> コンテンツ2 </div> </div> ); } } Clips.propTypes = { location: PropTypes.shape().isRequired, };
ルーティングに併せてスクロールできるパッケージもありますね。 こちらは座標指定が必要なので、併せて利用できるかと思います。 www.npmjs.com
ES6におけるReactコンポーネントの書き換えについて
用途に併せてコンポーネントを書き分けよう
eslintに従いクラスコンポーネントを書き換えるケースがよくあるので、サンプルを貼っておきます。 たくさん書いてありますが、全て結果としては同じ内容を表現しています。
クラスコンポーネント(class)
- 以下のようなクラスを作るのが最も万能ですが、内容に対してコードが長くなる場合があります。
import React, { Component } from 'react'; export default class App extends Component { render() { return ( <div>Hello {this.props.name} !</div> ); } } App.propTypes = { myName: React.PropTypes.string.isRequired, };
メソッドコンポーネント(function)
- メソッド単体でもコンポーネントとして利用できます。
- { myName }はpropsオブジェクト下のキーを具体的に定義しています。
- 省略していますが、第1引数propsの後に第2引数contextを受け取ることができます。
import React from 'react'; export default function App({ myName }) { return ( <div>Hello {myName} !</div> ); } App.propTypes = { myName: React.PropTypes.string.isRequired, };
メソッドコンポーネント(const)
- 上記をアロー関数で置き換えたものをconst値として渡しています。
- constは直接defaultのexportに設定できませんが、以下のように書くとdefaultに設定できます。
import React from 'react'; const App = ({ myName }) => { return ( <div>Hello {myName} !</div> ); }; App.propTypes = { myName: React.PropTypes.string.isRequired, }; export default App;
- コンポーネントの戻り値は必ずjsxなので、処理が複雑でない限りは以下のような書き方がベストです。
import React from 'react'; const App = ({ myName }) => ( <div>Hello {myName} !</div> ); App.propTypes = { myName: React.PropTypes.string.isRequired, }; export default App;
- 丸括弧がアロー関数の戻り値を表していたり、JSXを内包していたりとわかりづらいですが、どちらも省略可能なので、以下のようにも書けたりします。
- ここまで省略すると若干読みづらい感じもします。
import React from 'react'; const App = ({ myName }) => <div>Hello {myName} !</div>; App.propTypes = { myName: React.PropTypes.string.isRequired, }; export default App;
Mac新OS「Sierra」で内蔵カメラが動かなくなった件
今回「Sierra」へのアップデートでは早くもいくつか辛酸を味わっていますが、FaceTimeカメラについてはようやく解決したのでメモを残しておきます。
以下、やってみたこと。
VDCAssistantのプロセス強制終了
カメラを握っているプロセスがあることで、他で利用できなくなっている可能性があるそうです。 以下のコマンドを実行後に再起動をおこないます。 わたしは上記では解決しませせんでした。
sudo killall VDCAssistant
Macで急にFaceTimeカメラが認識されなくなった時の対処法 - Qiita
SMCリセット
電源を切った状態からCtrl + Shift + Option と電源ボタンを押す。 こちらも改善には至りませんでした。
セーフモード再起動
ここで解決しました。 こちらは起動のアップルロゴ表示時にDキーを押して立ち上げます。 動きがカクカクしたセーフモードで起動しアプリ「PhotoBooth」でカメラをチェックすると、何事もなかったかのように動いていました。 その後、再起動をおこない通常モードで起動、この時点でカメラは正常動作していました。
カメラのアクセス権がどこかで握られていたか、設定のキャッシュが残っていたかなど定かではありませんが、類似の事象でお困りの方は上記のいずれかで改善されるかもしれないので試してみてください。 (他の情報があればコメント等いただければ幸いです)
React開発をwebpackでおこなう
概要
前回までの記事でgulpを利用しての開発を進めていましたが、いろいろ見聞してwebpackを使ってみることにしました。
利点を簡潔に言えば、jsをひとつに纏めるかファイル群としてストアするかの選択ができます。さらにwebpack-dev-serverと組み合わせれば、実ファイルを都度生成することなくローカルサーバーで試走が行えるので、タスクランナーとしてgulpと入れ替えても事足りると思います。
最低限のサンプル
以下のことをできる最低限の構成です。
- EcmaScript6、JSXのトランスパイル実行
- ローカルサーバーでの試走
全体構成
─ / └ src/ │ └ entry.jsx │ └ index.html └ .babelrc └ package.json └ web.config.js
コード本体
シンプルにベースのindex.htmlにentry.jsxをロードするだけのサンプルです。 動作すれば画面に「Hello World」が表示されます。
index.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Hello World</title> </head> <body> <div id="app"></div> <script src="./bundle.js"></script> </body> </html>
entry.jsx
import React from 'react'; import { render } from 'react-dom'; render(( <div> Hello World </div> ), document.getElementById('app'));
セッティング
以下のコンフィグで行われる処理は次のとおりです。
- entryファイルとしてsrc下のindex.html、entry.jsxを取り込む。
- jsxを起点にあとの処理でimportされるファイルは全て自動的にトランスパイル対象になる。
- htmlファイルは同名でそのまま出力する。
- jsxファイルはトランスパイルして出力する、ただしnode_modules下は対象外にする。
webpack.config.js
module.exports = { context: __dirname + '/src', entry: { jsx: "./entry.jsx", html: "./index.html", }, output: { path: __dirname + '/dist', filename: 'bundle.js', publicPath: '/', }, module: { loaders: [ { test: /\.html$/, loader: "file?name=[name].[ext]" }, { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel' }, ] }, resolve: { extensions: ['', '.js', '.jsx'] }, };
補足
- loader指定する際はnpmパッケージ名末尾から「-loader」を省略して書けるので、上記に当てられている正確なパッケージ名はfile-loader・babel-loaderです。
- extensionsに記載された拡張子はjsx内のimport対象を探索する際のパターンになります。
import Test from './test'; // 上記の場合、「./test」「./test.js」「./test.jsx」のいずれかを探す。 // ファイルタイプ別で重複命名があるとエラーになってしまうので注意
.babelrc
{ "presets": ["react", "es2015"] }
loader設定時にオプション指定することもできるが、別ファイルを用意したほうが役割がはっきりするので分割しました。 webpackのコンフィグ側に記載する場合は以下のように記載できます。
: loaders: [ { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel?presets[]=react&presets[]=es2015' }, ] :
package.json
- 上記コード、トランスパイラで必要なパッケージを纏めました。
- また、scriptsにはトランスパイル実行(ビルド)とローカルサーバー起動コマンドを記載してみました。
{ "name": "hello", "version": "1.0.0", "description": "", "main": "entry.jsx", "scripts": { "build": "webpack --progress --colors", "start": "webpack-dev-server --hot --inline --progress --colors" }, "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.17.0", "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.16.0", "babel-preset-react": "^6.16.0", "file-loader": "^0.9.0", "webpack": "^1.13.2", "webpack-dev-server": "^1.16.2" }, "dependencies": { "react": "^15.3.2", "react-dom": "^15.3.2" } }
実行コマンド
npm install npm run start npm run build
ローカルサーバーはデフォルト http://localhost:8080/ が起点となります。
【補足】ESLintもフックしておく
WebpackでESLintを実装しておくと、ビルドやウォッチのタイミングでチェックが行われるためきれいなコードが書けるかと思います。 こちらも有用なので、メモ代わりに載せておくことにしました。
導入手順
- ESLintを設定します。セットアップは公式ドキュメントが参考になります。
- webpack.config.jsに以下のとおり加筆します。
--- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,9 @@ module.exports = { publicPath: '/', }, module: { + preLoaders: [ + { test: /\.jsx$/, loader: "eslint" }, + ], loaders: [ { test: /\.html$/, loader: "file?name=[name].[ext]" }, { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel' },
- eslint-loaderも忘れずにインストールします。
npm i -D eslint-loader
- Webpack実行時にエラーがあれば以下のように表示されます。
- 以下はbrowser環境変数をエスケープせずに"document"を参照するコードが内包されていた例
入門 React ―コンポーネントベースのWebフロントエンド開発
- 作者: Frankie Bagnardi,Jonathan Beebe,Richard Feldman,Tom Hallett,Simon HØjberg,Karl Mikkelsen,宮崎空
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/04/03
- メディア: 大型本
- この商品を含むブログ (2件) を見る